Merge branch 'master' into windows
Signed-off-by: Andrew Calcutt <acalcutt@techidiots.net>
This commit is contained in:
commit
e0d1a1c708
13 changed files with 701 additions and 681 deletions
|
@ -28,8 +28,8 @@ 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.
|
[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
|
docker run --rm -it -v $(pwd):/data -p 8080:8080 tileserver-gl-light
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
You can read full documentation of this project at https://tileserver.readthedocs.io/.
|
You can read full documentation of this project at https://maptiler-tileserver.readthedocs.io/
|
||||||
|
|
|
@ -132,6 +132,19 @@ If you have plenty of memory, try setting these equal to or slightly above your
|
||||||
If you need to conserve memory, try lower values for scale factors that are less common.
|
If you need to conserve memory, try lower values for scale factors that are less common.
|
||||||
Default is ``[16, 8, 4]``.
|
Default is ``[16, 8, 4]``.
|
||||||
|
|
||||||
|
``pbfAlias``
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Some CDNs did not handle .pbf extension as a static file correctly.
|
||||||
|
The default URLs (with .pbf) are always available, but an alternative can be set.
|
||||||
|
An example extension suffix would be ".pbf.pict".
|
||||||
|
|
||||||
|
``serveAllFonts``
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
If this option is enabled, all the fonts from the ``paths.fonts`` will be served.
|
||||||
|
Otherwise only the fonts referenced by available styles will be served.
|
||||||
|
|
||||||
``serveAllStyles``
|
``serveAllStyles``
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
@ -139,10 +152,16 @@ If this option is enabled, all the styles from the ``paths.styles`` will be serv
|
||||||
The process will also watch for changes in this directory and remove/add more styles dynamically.
|
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.
|
It is recommended to also use the ``serveAllFonts`` option when using this option.
|
||||||
|
|
||||||
|
``serveStaticMaps``
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
If this option is enabled, all the static map endpoints will be served.
|
||||||
|
Default is ``true``.
|
||||||
|
|
||||||
``watermark``
|
``watermark``
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Optional string to be rendered into the raster tiles (and static maps) as watermark (bottom-left corner).
|
Optional string to be rendered into the raster tiles and static maps as watermark (bottom-left corner).
|
||||||
Not used by default.
|
Not used by default.
|
||||||
|
|
||||||
``staticAttributionText``
|
``staticAttributionText``
|
||||||
|
|
|
@ -52,14 +52,14 @@ Static images
|
||||||
|
|
||||||
* can be provided multiple times
|
* can be provided multiple times
|
||||||
|
|
||||||
* ``latlng`` - indicates coordinates are in ``lat,lng`` order rather than the usual ``lng,lat``
|
* ``latlng`` - indicates coordinates are in ``lat,lng`` order rather than the usual ``lng,lat`` for paths and markers
|
||||||
* ``fill`` - color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``)
|
* ``fill`` - default color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``) for all paths
|
||||||
* ``stroke`` - color of the path stroke
|
* ``stroke`` - default color of the path stroke for all paths
|
||||||
* ``width`` - width of the stroke
|
* ``width`` - default width of the stroke for all paths
|
||||||
* ``linecap`` - rendering style for the start and end points of the path
|
* ``linecap`` - rendering style for the start and end points of all paths - see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
||||||
* ``linejoin`` - rendering style for overlapping segments of the path with differing directions
|
* ``linejoin`` - rendering style for joining successive segments of all paths when the direction changes - see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
||||||
* ``border`` - color of the optional border path stroke
|
* ``border`` - color of the optional border stroke for all paths ; the border is like a halo around the stroke
|
||||||
* ``borderwidth`` - width of the border stroke (default 10% of width)
|
* ``borderwidth`` - width of the border stroke (default 10% of stroke width) for all paths
|
||||||
* ``marker`` - Marker in format ``lng,lat|iconPath|option|option|...``
|
* ``marker`` - Marker in format ``lng,lat|iconPath|option|option|...``
|
||||||
|
|
||||||
* Will be rendered with the bottom center at the provided location
|
* Will be rendered with the bottom center at the provided location
|
||||||
|
|
286
package-lock.json
generated
286
package-lock.json
generated
|
@ -16,7 +16,7 @@
|
||||||
"@mapbox/vector-tile": "1.3.1",
|
"@mapbox/vector-tile": "1.3.1",
|
||||||
"@maplibre/maplibre-gl-native": "5.2.0",
|
"@maplibre/maplibre-gl-native": "5.2.0",
|
||||||
"@maplibre/maplibre-gl-style-spec": "18.0.0",
|
"@maplibre/maplibre-gl-style-spec": "18.0.0",
|
||||||
"@sindresorhus/fnv1a": "3.0.0",
|
"@sindresorhus/fnv1a": "3.1.0",
|
||||||
"advanced-pool": "0.3.3",
|
"advanced-pool": "0.3.3",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"canvas": "2.11.2",
|
"canvas": "2.11.2",
|
||||||
|
@ -40,12 +40,12 @@
|
||||||
"tileserver-gl": "src/main.js"
|
"tileserver-gl": "src/main.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^18.4.1",
|
"@commitlint/cli": "^18.4.3",
|
||||||
"@commitlint/config-conventional": "^18.4.0",
|
"@commitlint/config-conventional": "^18.4.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||||
"@typescript-eslint/parser": "^6.11.0",
|
"@typescript-eslint/parser": "^6.12.0",
|
||||||
"chai": "4.3.10",
|
"chai": "4.3.10",
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-jsdoc": "^46.9.0",
|
"eslint-plugin-jsdoc": "^46.9.0",
|
||||||
"eslint-plugin-prettier": "^5.0.1",
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
|
@ -207,16 +207,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/cli": {
|
"node_modules/@commitlint/cli": {
|
||||||
"version": "18.4.1",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.4.3.tgz",
|
||||||
"integrity": "sha512-4+jljfd29Udw9RDDyigavLO9LvdbmB8O9xjDzVZ0R3lJuG7nCeyHgnKWIVpFaN590isZMV/cMeQK0gH7hRF40A==",
|
"integrity": "sha512-zop98yfB3A6NveYAZ3P1Mb6bIXuCeWgnUfVNkH4yhIMQpQfzFwseadazOuSn0OOfTt0lWuFauehpm9GcqM5lww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/format": "^18.4.0",
|
"@commitlint/format": "^18.4.3",
|
||||||
"@commitlint/lint": "^18.4.0",
|
"@commitlint/lint": "^18.4.3",
|
||||||
"@commitlint/load": "^18.4.1",
|
"@commitlint/load": "^18.4.3",
|
||||||
"@commitlint/read": "^18.4.0",
|
"@commitlint/read": "^18.4.3",
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"execa": "^5.0.0",
|
"execa": "^5.0.0",
|
||||||
"lodash.isfunction": "^3.0.9",
|
"lodash.isfunction": "^3.0.9",
|
||||||
"resolve-from": "5.0.0",
|
"resolve-from": "5.0.0",
|
||||||
|
@ -231,9 +231,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/config-conventional": {
|
"node_modules/@commitlint/config-conventional": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.4.3.tgz",
|
||||||
"integrity": "sha512-vArwCZopsZs0FnGsh9AR7uUTPZ5oVGk8+qnEZWq2KTsMjrE0k80b+oZ32GSQmXQT2iMKVrDC8pKX5uKNkCe9Sw==",
|
"integrity": "sha512-729eRRaNta7JZF07qf6SAGSghoDEp9mH7yHU0m7ff0q89W97wDrWCyZ3yoV3mcQJwbhlmVmZPTkPcm7qiAu8WA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"conventional-changelog-conventionalcommits": "^7.0.2"
|
"conventional-changelog-conventionalcommits": "^7.0.2"
|
||||||
|
@ -243,12 +243,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/config-validator": {
|
"node_modules/@commitlint/config-validator": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.4.3.tgz",
|
||||||
"integrity": "sha512-1y6qHMU3o4cYQSK+Y9EnmH6H1GRiwQGjnLIUOIKlekrmfc8MrMk1ByNmb8od4vK3qHJAaL/77/5n+1uyyIF5dA==",
|
"integrity": "sha512-FPZZmTJBARPCyef9ohRC9EANiQEKSWIdatx5OlgeHKu878dWwpyeFauVkhzuBRJFcCA4Uvz/FDtlDKs008IHcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"ajv": "^8.11.0"
|
"ajv": "^8.11.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -256,12 +256,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/ensure": {
|
"node_modules/@commitlint/ensure": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.4.3.tgz",
|
||||||
"integrity": "sha512-N5cJo/n61ULSwz3W5Iz/IZJ0I9H/PaHc+OMcF2XcRVbLa6B3YwzEW66XGCRKVULlsBNSrIH6tk5un9ayXAXIdw==",
|
"integrity": "sha512-MI4fwD9TWDVn4plF5+7JUyLLbkOdzIRBmVeNlk4dcGlkrVA+/l5GLcpN66q9LkFsFv6G2X31y89ApA3hqnqIFg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"lodash.camelcase": "^4.3.0",
|
"lodash.camelcase": "^4.3.0",
|
||||||
"lodash.kebabcase": "^4.1.1",
|
"lodash.kebabcase": "^4.1.1",
|
||||||
"lodash.snakecase": "^4.1.1",
|
"lodash.snakecase": "^4.1.1",
|
||||||
|
@ -273,21 +273,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/execute-rule": {
|
"node_modules/@commitlint/execute-rule": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.4.3.tgz",
|
||||||
"integrity": "sha512-g013SWki6ZWhURBLOSXTaVQGWHdA0QlPJGiW4a+YpThezmJOemvc4LiKVpn13AjSKQ40QnmBqpBrxujOaSo+3A==",
|
"integrity": "sha512-t7FM4c+BdX9WWZCPrrbV5+0SWLgT3kCq7e7/GhHCreYifg3V8qyvO127HF796vyFql75n4TFF+5v1asOOWkV1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=v18"
|
"node": ">=v18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/format": {
|
"node_modules/@commitlint/format": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.4.3.tgz",
|
||||||
"integrity": "sha512-MiAe4D5/ahty38CzULdQbpRa3ReKZtx0kyigOWcntq+N5uqez+Ac4/MO7H+3j1kC4G7nfJVfBu6TqcXeyNvhCQ==",
|
"integrity": "sha512-8b+ItXYHxAhRAXFfYki5PpbuMMOmXYuzLxib65z2XTqki59YDQJGpJ/wB1kEE5MQDgSTQWtKUrA8n9zS/1uIDQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"chalk": "^4.1.0"
|
"chalk": "^4.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -295,12 +295,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/is-ignored": {
|
"node_modules/@commitlint/is-ignored": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.4.3.tgz",
|
||||||
"integrity": "sha512-vyBKBj3Q4N3Xe4ZQcJXW9ef6gVrDL9Fl2HXnnC3F0Qt/F6E4runhJkEuUh5DB3WCXTJUHIJkByKPqrnz4RNrZw==",
|
"integrity": "sha512-ZseOY9UfuAI32h9w342Km4AIaTieeFskm2ZKdrG7r31+c6zGBzuny9KQhwI9puc0J3GkUquEgKJblCl7pMnjwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"semver": "7.5.4"
|
"semver": "7.5.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -308,30 +308,30 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/lint": {
|
"node_modules/@commitlint/lint": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.4.3.tgz",
|
||||||
"integrity": "sha512-Wkkf1DPVeLdHYGqtzMBfWoMbUtCojvlzDR89OKVic1rid41iZbb0FzTcwgMYs/1TNWNxoIq9PVVwY7ovLX1aJQ==",
|
"integrity": "sha512-18u3MRgEXNbnYkMOWoncvq6QB8/90m9TbERKgdPqVvS+zQ/MsuRhdvHYCIXGXZxUb0YI4DV2PC4bPneBV/fYuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/is-ignored": "^18.4.0",
|
"@commitlint/is-ignored": "^18.4.3",
|
||||||
"@commitlint/parse": "^18.4.0",
|
"@commitlint/parse": "^18.4.3",
|
||||||
"@commitlint/rules": "^18.4.0",
|
"@commitlint/rules": "^18.4.3",
|
||||||
"@commitlint/types": "^18.4.0"
|
"@commitlint/types": "^18.4.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=v18"
|
"node": ">=v18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/load": {
|
"node_modules/@commitlint/load": {
|
||||||
"version": "18.4.1",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.4.3.tgz",
|
||||||
"integrity": "sha512-o/plBiPJQgbSq/4ipDpsq4HCmURjBAEjr1EO/p2falr3VhwV0WGXTvb8NlihgI8xtSyO6lHvtycrE535GMLQbA==",
|
"integrity": "sha512-v6j2WhvRQJrcJaj5D+EyES2WKTxPpxENmNpNG3Ww8MZGik3jWRXtph0QTzia5ZJyPh2ib5aC/6BIDymkUUM58Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/config-validator": "^18.4.0",
|
"@commitlint/config-validator": "^18.4.3",
|
||||||
"@commitlint/execute-rule": "^18.4.0",
|
"@commitlint/execute-rule": "^18.4.3",
|
||||||
"@commitlint/resolve-extends": "^18.4.0",
|
"@commitlint/resolve-extends": "^18.4.3",
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"@types/node": "^18.11.9",
|
"@types/node": "^18.11.9",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"cosmiconfig": "^8.3.6",
|
"cosmiconfig": "^8.3.6",
|
||||||
|
@ -346,22 +346,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/message": {
|
"node_modules/@commitlint/message": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.4.3.tgz",
|
||||||
"integrity": "sha512-3kg6NQO6pJ+VdBTWi51KInT8ngkxPJaW+iI7URtUALjKcO9K4XY3gf80ZPmS1hDessrjb7qCr1lau8eWMINAQw==",
|
"integrity": "sha512-ddJ7AztWUIoEMAXoewx45lKEYEOeOlBVWjk8hDMUGpprkuvWULpaXczqdjwVtjrKT3JhhN+gMs8pm5G3vB2how==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=v18"
|
"node": ">=v18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/parse": {
|
"node_modules/@commitlint/parse": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.4.3.tgz",
|
||||||
"integrity": "sha512-SxTCSUZH8CJNYWOlFg18YUQ2RLz8ubXKbpHUIiSNwCbiQx7UDCydp1JnhoB4sOYOxgV8d3nuDwYluRU5KnEY4A==",
|
"integrity": "sha512-eoH7CXM9L+/Me96KVcfJ27EIIbA5P9sqw3DqjJhRYuhaULIsPHFs5S5GBDCqT0vKZQDx0DgxhMpW6AQbnKrFtA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"conventional-changelog-angular": "^6.0.0",
|
"conventional-changelog-angular": "^7.0.0",
|
||||||
"conventional-commits-parser": "^5.0.0"
|
"conventional-commits-parser": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -369,13 +369,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/read": {
|
"node_modules/@commitlint/read": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.4.3.tgz",
|
||||||
"integrity": "sha512-IpnABCbDeOw5npZ09SZZGLfd3T7cFtsxUYm6wT3aGmIB2fXKE3fMeuj3jxXjMibiGIyA3Z5voCMuOcKWpkNySA==",
|
"integrity": "sha512-H4HGxaYA6OBCimZAtghL+B+SWu8ep4X7BwgmedmqWZRHxRLcX2q0bWBtUm5FsMbluxbOfrJwOs/Z0ah4roP/GQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/top-level": "^18.4.0",
|
"@commitlint/top-level": "^18.4.3",
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"fs-extra": "^11.0.0",
|
"fs-extra": "^11.0.0",
|
||||||
"git-raw-commits": "^2.0.11",
|
"git-raw-commits": "^2.0.11",
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
|
@ -385,13 +385,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/resolve-extends": {
|
"node_modules/@commitlint/resolve-extends": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.4.3.tgz",
|
||||||
"integrity": "sha512-qhgU6ach+S6sJMD9NjCYiEycOObGhxzWQLQzqlScJCv9zkPs15Bg0ffLXTQ3z7ipXv46XEKYMnSJzjLRw2Tlkg==",
|
"integrity": "sha512-30sk04LZWf8+SDgJrbJCjM90gTg2LxsD9cykCFeFu+JFHvBFq5ugzp2eO/DJGylAdVaqxej3c7eTSE64hR/lnw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/config-validator": "^18.4.0",
|
"@commitlint/config-validator": "^18.4.3",
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"import-fresh": "^3.0.0",
|
"import-fresh": "^3.0.0",
|
||||||
"lodash.mergewith": "^4.6.2",
|
"lodash.mergewith": "^4.6.2",
|
||||||
"resolve-from": "^5.0.0",
|
"resolve-from": "^5.0.0",
|
||||||
|
@ -402,15 +402,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/rules": {
|
"node_modules/@commitlint/rules": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.4.3.tgz",
|
||||||
"integrity": "sha512-T3ChRxQZ6g0iNCpVLc6KeQId0/86TnyQA8PFkng+dWElO2DAA5km/yirgKZV1Xlc+gF7Rf6d+a0ottxdKpOY+w==",
|
"integrity": "sha512-8KIeukDf45BiY+Lul1T0imSNXF0sMrlLG6JpLLKolkmYVQ6PxxoNOriwyZ3UTFFpaVbPy0rcITaV7U9JCAfDTA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@commitlint/ensure": "^18.4.0",
|
"@commitlint/ensure": "^18.4.3",
|
||||||
"@commitlint/message": "^18.4.0",
|
"@commitlint/message": "^18.4.3",
|
||||||
"@commitlint/to-lines": "^18.4.0",
|
"@commitlint/to-lines": "^18.4.3",
|
||||||
"@commitlint/types": "^18.4.0",
|
"@commitlint/types": "^18.4.3",
|
||||||
"execa": "^5.0.0"
|
"execa": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -418,18 +418,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/to-lines": {
|
"node_modules/@commitlint/to-lines": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.4.3.tgz",
|
||||||
"integrity": "sha512-bZXuCtfBPjNgtEnG3gwJrveIgfKK2UdhIhFvKpMTrQl/gAwoto/3mzmE7qGAHwmuP4eZ2U8X7iwMnqIlWmv2Tw==",
|
"integrity": "sha512-fy1TAleik4Zfru1RJ8ZU6cOSvgSVhUellxd3WZV1D5RwHZETt1sZdcA4mQN2y3VcIZsUNKkW0Mq8CM9/L9harQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=v18"
|
"node": ">=v18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/top-level": {
|
"node_modules/@commitlint/top-level": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.4.3.tgz",
|
||||||
"integrity": "sha512-TfulcA8UHF7MZ6tm4Ci3aqZgMBZa1OoCg4prccWHvwG/hsHujZ7+0FKbeKqDbcSli/YWm4NJwEjl4uh5itIJeA==",
|
"integrity": "sha512-E6fJPBLPFL5R8+XUNSYkj4HekIOuGMyJo3mIx2PkYc3clel+pcWQ7TConqXxNWW4x1ugigiIY2RGot55qUq1hw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"find-up": "^5.0.0"
|
"find-up": "^5.0.0"
|
||||||
|
@ -439,9 +439,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/types": {
|
"node_modules/@commitlint/types": {
|
||||||
"version": "18.4.0",
|
"version": "18.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.4.3.tgz",
|
||||||
"integrity": "sha512-MKeaFxt0I9fhqUb2E+YIzX/gZtmkuodJET/XKiZIMvXUff8Ee4Ih86eLg+yAm2jf1pwGBmU02uNOp0y094w2Uw==",
|
"integrity": "sha512-cvzx+vtY/I2hVBZHCLrpoh+sA0hfuzHwDc+BAFPimYLjJkpHnghQM+z8W/KyLGkygJh3BtI3xXXq+dKjnSWEmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.1.0"
|
"chalk": "^4.1.0"
|
||||||
|
@ -556,9 +556,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.53.0",
|
"version": "8.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
|
||||||
"integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
|
"integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
@ -969,9 +969,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sindresorhus/fnv1a": {
|
"node_modules/@sindresorhus/fnv1a": {
|
||||||
"version": "3.0.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/fnv1a/-/fnv1a-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/fnv1a/-/fnv1a-3.1.0.tgz",
|
||||||
"integrity": "sha512-M6pmbdZqAryzjZ4ELAzrdCMoMZk5lH/fshKrapfSeXdf2W+GDqZvPmfXaNTZp43//FVbSwkTPwpEMnehSyskkQ==",
|
"integrity": "sha512-KV321z5m/0nuAg83W1dPLy85HpHDk7Sdi4fJbwvacWsEhAh+rZUW4ZfGcXmUIvjZg4ss2bcwNlRhJ7GBEUG08w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
},
|
},
|
||||||
|
@ -1005,9 +1005,9 @@
|
||||||
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
|
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.18.9",
|
"version": "18.18.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.11.tgz",
|
||||||
"integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==",
|
"integrity": "sha512-c1vku6qnTeujJneYH94/4aq73XrVcsJe35UPyAsSok1ijiKrkRzK+AxQPSpNMUnC03roWBBwJx/9I8V7lQoxmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
|
@ -1025,16 +1025,16 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz",
|
||||||
"integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==",
|
"integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.5.1",
|
"@eslint-community/regexpp": "^4.5.1",
|
||||||
"@typescript-eslint/scope-manager": "6.11.0",
|
"@typescript-eslint/scope-manager": "6.12.0",
|
||||||
"@typescript-eslint/type-utils": "6.11.0",
|
"@typescript-eslint/type-utils": "6.12.0",
|
||||||
"@typescript-eslint/utils": "6.11.0",
|
"@typescript-eslint/utils": "6.12.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.11.0",
|
"@typescript-eslint/visitor-keys": "6.12.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.2.4",
|
"ignore": "^5.2.4",
|
||||||
|
@ -1060,15 +1060,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz",
|
||||||
"integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==",
|
"integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "6.11.0",
|
"@typescript-eslint/scope-manager": "6.12.0",
|
||||||
"@typescript-eslint/types": "6.11.0",
|
"@typescript-eslint/types": "6.12.0",
|
||||||
"@typescript-eslint/typescript-estree": "6.11.0",
|
"@typescript-eslint/typescript-estree": "6.12.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.11.0",
|
"@typescript-eslint/visitor-keys": "6.12.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1088,13 +1088,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz",
|
||||||
"integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==",
|
"integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.11.0",
|
"@typescript-eslint/types": "6.12.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.11.0"
|
"@typescript-eslint/visitor-keys": "6.12.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.0.0 || >=18.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
|
@ -1105,13 +1105,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz",
|
||||||
"integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==",
|
"integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "6.11.0",
|
"@typescript-eslint/typescript-estree": "6.12.0",
|
||||||
"@typescript-eslint/utils": "6.11.0",
|
"@typescript-eslint/utils": "6.12.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^1.0.1"
|
"ts-api-utils": "^1.0.1"
|
||||||
},
|
},
|
||||||
|
@ -1132,9 +1132,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz",
|
||||||
"integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==",
|
"integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.0.0 || >=18.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
|
@ -1145,13 +1145,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz",
|
||||||
"integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==",
|
"integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.11.0",
|
"@typescript-eslint/types": "6.12.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.11.0",
|
"@typescript-eslint/visitor-keys": "6.12.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
|
@ -1172,17 +1172,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz",
|
||||||
"integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==",
|
"integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@types/json-schema": "^7.0.12",
|
"@types/json-schema": "^7.0.12",
|
||||||
"@types/semver": "^7.5.0",
|
"@types/semver": "^7.5.0",
|
||||||
"@typescript-eslint/scope-manager": "6.11.0",
|
"@typescript-eslint/scope-manager": "6.12.0",
|
||||||
"@typescript-eslint/types": "6.11.0",
|
"@typescript-eslint/types": "6.12.0",
|
||||||
"@typescript-eslint/typescript-estree": "6.11.0",
|
"@typescript-eslint/typescript-estree": "6.12.0",
|
||||||
"semver": "^7.5.4"
|
"semver": "^7.5.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1197,12 +1197,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "6.11.0",
|
"version": "6.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz",
|
||||||
"integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==",
|
"integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.11.0",
|
"@typescript-eslint/types": "6.12.0",
|
||||||
"eslint-visitor-keys": "^3.4.1"
|
"eslint-visitor-keys": "^3.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2117,15 +2117,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/conventional-changelog-angular": {
|
"node_modules/conventional-changelog-angular": {
|
||||||
"version": "6.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
|
||||||
"integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==",
|
"integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"compare-func": "^2.0.0"
|
"compare-func": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/conventional-changelog-conventionalcommits": {
|
"node_modules/conventional-changelog-conventionalcommits": {
|
||||||
|
@ -2809,15 +2809,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.53.0",
|
"version": "8.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
|
||||||
"integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
|
"integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
"@eslint/eslintrc": "^2.1.3",
|
"@eslint/eslintrc": "^2.1.3",
|
||||||
"@eslint/js": "8.53.0",
|
"@eslint/js": "8.54.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.13",
|
"@humanwhocodes/config-array": "^0.11.13",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
|
|
16
package.json
16
package.json
|
@ -6,7 +6,7 @@
|
||||||
"bin": "src/main.js",
|
"bin": "src/main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha test/**.js --timeout 10000",
|
"test": "mocha test/**.js --timeout 10000 --exit",
|
||||||
"lint:yml": "yamllint --schema=CORE_SCHEMA *.{yml,yaml}",
|
"lint:yml": "yamllint --schema=CORE_SCHEMA *.{yml,yaml}",
|
||||||
"lint:js": "npm run lint:eslint && npm run lint:prettier",
|
"lint:js": "npm run lint:eslint && npm run lint:prettier",
|
||||||
"lint:js:fix": "npm run lint:eslint:fix && npm run lint:prettier:fix",
|
"lint:js:fix": "npm run lint:eslint:fix && npm run lint:prettier:fix",
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
"lint:eslint:fix": "eslint --fix \"{,!(node_modules|dist|static|public)/**/}*.{js,ts,cjs,mjs}\" --ignore-path .gitignore",
|
"lint:eslint:fix": "eslint --fix \"{,!(node_modules|dist|static|public)/**/}*.{js,ts,cjs,mjs}\" --ignore-path .gitignore",
|
||||||
"lint:prettier": "prettier --check \"{,!(node_modules|dist|static|public)/**/}*.{js,ts,cjs,mjs,json}\" --ignore-path .gitignore",
|
"lint:prettier": "prettier --check \"{,!(node_modules|dist|static|public)/**/}*.{js,ts,cjs,mjs,json}\" --ignore-path .gitignore",
|
||||||
"lint:prettier:fix": "prettier --write \"{,!(node_modules|dist|static|public)/**/}*.{js,ts,cjs,mjs,json}\" --ignore-path .gitignore",
|
"lint:prettier:fix": "prettier --write \"{,!(node_modules|dist|static|public)/**/}*.{js,ts,cjs,mjs,json}\" --ignore-path .gitignore",
|
||||||
"docker": "docker build -f Dockerfile . && docker run --rm -i -p 8080:8080 $(docker build -q .)",
|
"docker": "docker build . && docker run --rm -i -p 8080:8080 $(docker build -q .)",
|
||||||
"prepare": "node -e \"if (process.env.NODE_ENV !== 'production'){ process.exit(1) } \" || husky install"
|
"prepare": "node -e \"if (process.env.NODE_ENV !== 'production'){ process.exit(1) } \" || husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
"@mapbox/vector-tile": "1.3.1",
|
"@mapbox/vector-tile": "1.3.1",
|
||||||
"@maplibre/maplibre-gl-native": "5.2.0",
|
"@maplibre/maplibre-gl-native": "5.2.0",
|
||||||
"@maplibre/maplibre-gl-style-spec": "18.0.0",
|
"@maplibre/maplibre-gl-style-spec": "18.0.0",
|
||||||
"@sindresorhus/fnv1a": "3.0.0",
|
"@sindresorhus/fnv1a": "3.1.0",
|
||||||
"advanced-pool": "0.3.3",
|
"advanced-pool": "0.3.3",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"canvas": "2.11.2",
|
"canvas": "2.11.2",
|
||||||
|
@ -46,12 +46,12 @@
|
||||||
"tileserver-gl-styles": "2.0.0"
|
"tileserver-gl-styles": "2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^18.4.1",
|
"@commitlint/cli": "^18.4.3",
|
||||||
"@commitlint/config-conventional": "^18.4.0",
|
"@commitlint/config-conventional": "^18.4.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||||
"@typescript-eslint/parser": "^6.11.0",
|
"@typescript-eslint/parser": "^6.12.0",
|
||||||
"chai": "4.3.10",
|
"chai": "4.3.10",
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-jsdoc": "^46.9.0",
|
"eslint-plugin-jsdoc": "^46.9.0",
|
||||||
"eslint-plugin-prettier": "^5.0.1",
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
|
|
|
@ -82,9 +82,6 @@
|
||||||
<div class="identifier">type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data {{#if source_type}} | ext: {{source_type}}{{/if}}</div>
|
<div class="identifier">type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data {{#if source_type}} | ext: {{source_type}}{{/if}}</div>
|
||||||
<p class="services">
|
<p class="services">
|
||||||
services: <a href="{{public_url}}data/{{@key}}.json{{&../key_query}}">TileJSON</a>
|
services: <a href="{{public_url}}data/{{@key}}.json{{&../key_query}}">TileJSON</a>
|
||||||
{{#if wmts_link}}
|
|
||||||
| <a href="{{&wmts_link}}">WMTS</a>
|
|
||||||
{{/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;" />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
var options = {
|
const options = {
|
||||||
timeout: 2000,
|
timeout: 2000,
|
||||||
};
|
};
|
||||||
var url = 'http://localhost:8080/health';
|
const url = 'http://localhost:8080/health';
|
||||||
var request = http.request(url, options, (res) => {
|
const request = http.request(url, options, (res) => {
|
||||||
console.log(`STATUS: ${res.statusCode}`);
|
console.log(`STATUS: ${res.statusCode}`);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|
303
src/render.js
Normal file
303
src/render.js
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { createCanvas, Image } from 'canvas';
|
||||||
|
|
||||||
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||||
|
|
||||||
|
const mercator = new SphericalMercator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms coordinates to pixels.
|
||||||
|
* @param {List[Number]} ll Longitude/Latitude coordinate pair.
|
||||||
|
* @param {number} zoom Map zoom level.
|
||||||
|
*/
|
||||||
|
const precisePx = (ll, zoom) => {
|
||||||
|
const px = mercator.px(ll, 20);
|
||||||
|
const scale = Math.pow(2, zoom - 20);
|
||||||
|
return [px[0] * scale, px[1] * scale];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a marker in canvas context.
|
||||||
|
* @param {object} ctx Canvas context object.
|
||||||
|
* @param {object} marker Marker object parsed by extractMarkersFromQuery.
|
||||||
|
* @param {number} z Map zoom level.
|
||||||
|
*/
|
||||||
|
const drawMarker = (ctx, marker, z) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const img = new Image();
|
||||||
|
const pixelCoords = precisePx(marker.location, z);
|
||||||
|
|
||||||
|
const getMarkerCoordinates = (imageWidth, imageHeight, scale) => {
|
||||||
|
// Images are placed with their top-left corner at the provided location
|
||||||
|
// within the canvas but we expect icons to be centered and above it.
|
||||||
|
|
||||||
|
// Substract half of the images width from the x-coordinate to center
|
||||||
|
// the image in relation to the provided location
|
||||||
|
let xCoordinate = pixelCoords[0] - imageWidth / 2;
|
||||||
|
// Substract the images height from the y-coordinate to place it above
|
||||||
|
// the provided location
|
||||||
|
let yCoordinate = pixelCoords[1] - imageHeight;
|
||||||
|
|
||||||
|
// Since image placement is dependent on the size offsets have to be
|
||||||
|
// scaled as well. Additionally offsets are provided as either positive or
|
||||||
|
// negative values so we always add them
|
||||||
|
if (marker.offsetX) {
|
||||||
|
xCoordinate = xCoordinate + marker.offsetX * scale;
|
||||||
|
}
|
||||||
|
if (marker.offsetY) {
|
||||||
|
yCoordinate = yCoordinate + marker.offsetY * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: xCoordinate,
|
||||||
|
y: yCoordinate,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawOnCanvas = () => {
|
||||||
|
// Check if the images should be resized before beeing drawn
|
||||||
|
const defaultScale = 1;
|
||||||
|
const scale = marker.scale ? marker.scale : defaultScale;
|
||||||
|
|
||||||
|
// Calculate scaled image sizes
|
||||||
|
const imageWidth = img.width * scale;
|
||||||
|
const imageHeight = img.height * scale;
|
||||||
|
|
||||||
|
// Pass the desired sizes to get correlating coordinates
|
||||||
|
const coords = getMarkerCoordinates(imageWidth, imageHeight, scale);
|
||||||
|
|
||||||
|
// Draw the image on canvas
|
||||||
|
if (scale != defaultScale) {
|
||||||
|
ctx.drawImage(img, coords.x, coords.y, imageWidth, imageHeight);
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(img, coords.x, coords.y);
|
||||||
|
}
|
||||||
|
// Resolve the promise when image has been drawn
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onload = drawOnCanvas;
|
||||||
|
img.onerror = (err) => {
|
||||||
|
throw err;
|
||||||
|
};
|
||||||
|
img.src = marker.icon;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a list of markers onto a canvas.
|
||||||
|
* Wraps drawing of markers into list of promises and awaits them.
|
||||||
|
* It's required because images are expected to load asynchronous in canvas js
|
||||||
|
* even when provided from a local disk.
|
||||||
|
* @param {object} ctx Canvas context object.
|
||||||
|
* @param {List[Object]} markers Marker objects parsed by extractMarkersFromQuery.
|
||||||
|
* @param {number} z Map zoom level.
|
||||||
|
*/
|
||||||
|
const drawMarkers = async (ctx, markers, z) => {
|
||||||
|
const markerPromises = [];
|
||||||
|
|
||||||
|
for (const marker of markers) {
|
||||||
|
// Begin drawing marker
|
||||||
|
markerPromises.push(drawMarker(ctx, marker, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Await marker drawings before continuing
|
||||||
|
await Promise.all(markerPromises);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a list of coordinates onto a canvas and styles the resulting path.
|
||||||
|
* @param {object} ctx Canvas context object.
|
||||||
|
* @param {List[Number]} path List of coordinates.
|
||||||
|
* @param {object} query Request query parameters.
|
||||||
|
* @param {string} pathQuery Path query parameter.
|
||||||
|
* @param {number} z Map zoom level.
|
||||||
|
*/
|
||||||
|
const drawPath = (ctx, path, query, pathQuery, z) => {
|
||||||
|
const splitPaths = pathQuery.split('|');
|
||||||
|
|
||||||
|
if (!path || path.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
// Transform coordinates to pixel on canvas and draw lines between points
|
||||||
|
for (const pair of path) {
|
||||||
|
const px = precisePx(pair, z);
|
||||||
|
ctx.lineTo(px[0], px[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if first coordinate matches last coordinate
|
||||||
|
if (
|
||||||
|
path[0][0] === path[path.length - 1][0] &&
|
||||||
|
path[0][1] === path[path.length - 1][1]
|
||||||
|
) {
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally fill drawn shape with a rgba color from query
|
||||||
|
const pathHasFill = splitPaths.filter((x) => x.startsWith('fill')).length > 0;
|
||||||
|
if (query.fill !== undefined || pathHasFill) {
|
||||||
|
if ('fill' in query) {
|
||||||
|
ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
|
||||||
|
}
|
||||||
|
if (pathHasFill) {
|
||||||
|
ctx.fillStyle = splitPaths
|
||||||
|
.find((x) => x.startsWith('fill:'))
|
||||||
|
.replace('fill:', '');
|
||||||
|
}
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get line width from query and fall back to 1 if not provided
|
||||||
|
const pathHasWidth =
|
||||||
|
splitPaths.filter((x) => x.startsWith('width')).length > 0;
|
||||||
|
if (query.width !== undefined || pathHasWidth) {
|
||||||
|
let lineWidth = 1;
|
||||||
|
// Get line width from query
|
||||||
|
if ('width' in query) {
|
||||||
|
lineWidth = Number(query.width);
|
||||||
|
}
|
||||||
|
// Get line width from path in query
|
||||||
|
if (pathHasWidth) {
|
||||||
|
lineWidth = Number(
|
||||||
|
splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Get border width from query and fall back to 10% of line width
|
||||||
|
const borderWidth =
|
||||||
|
query.borderwidth !== undefined
|
||||||
|
? parseFloat(query.borderwidth)
|
||||||
|
: lineWidth * 0.1;
|
||||||
|
|
||||||
|
// Set rendering style for the start and end points of the path
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
||||||
|
ctx.lineCap = query.linecap || 'butt';
|
||||||
|
|
||||||
|
// Set rendering style for overlapping segments of the path with differing directions
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
||||||
|
ctx.lineJoin = query.linejoin || 'miter';
|
||||||
|
|
||||||
|
// In order to simulate a border we draw the path two times with the first
|
||||||
|
// beeing the wider border part.
|
||||||
|
if (query.border !== undefined && borderWidth > 0) {
|
||||||
|
// We need to double the desired border width and add it to the line width
|
||||||
|
// in order to get the desired border on each side of the line.
|
||||||
|
ctx.lineWidth = lineWidth + borderWidth * 2;
|
||||||
|
// Set border style as rgba
|
||||||
|
ctx.strokeStyle = query.border;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathHasStroke =
|
||||||
|
splitPaths.filter((x) => x.startsWith('stroke')).length > 0;
|
||||||
|
if (query.stroke !== undefined || pathHasStroke) {
|
||||||
|
if ('stroke' in query) {
|
||||||
|
ctx.strokeStyle = query.stroke;
|
||||||
|
}
|
||||||
|
// Path Stroke gets higher priority
|
||||||
|
if (pathHasStroke) {
|
||||||
|
ctx.strokeStyle = splitPaths
|
||||||
|
.find((x) => x.startsWith('stroke:'))
|
||||||
|
.replace('stroke:', '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderOverlay = async (
|
||||||
|
z,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
bearing,
|
||||||
|
pitch,
|
||||||
|
w,
|
||||||
|
h,
|
||||||
|
scale,
|
||||||
|
paths,
|
||||||
|
markers,
|
||||||
|
query,
|
||||||
|
) => {
|
||||||
|
if ((!paths || paths.length === 0) && (!markers || markers.length === 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const center = precisePx([x, y], z);
|
||||||
|
|
||||||
|
const mapHeight = 512 * (1 << z);
|
||||||
|
const maxEdge = center[1] + h / 2;
|
||||||
|
const minEdge = center[1] - h / 2;
|
||||||
|
if (maxEdge > mapHeight) {
|
||||||
|
center[1] -= maxEdge - mapHeight;
|
||||||
|
} else if (minEdge < 0) {
|
||||||
|
center[1] -= minEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas = createCanvas(scale * w, scale * h);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
if (bearing) {
|
||||||
|
ctx.translate(w / 2, h / 2);
|
||||||
|
ctx.rotate((-bearing / 180) * Math.PI);
|
||||||
|
ctx.translate(-center[0], -center[1]);
|
||||||
|
} else {
|
||||||
|
// optimized path
|
||||||
|
ctx.translate(-center[0] + w / 2, -center[1] + h / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw provided paths if any
|
||||||
|
paths.forEach((path, i) => {
|
||||||
|
const pathQuery = Array.isArray(query.path) ? query.path.at(i) : query.path;
|
||||||
|
drawPath(ctx, path, query, pathQuery, z);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Await drawing of markers before rendering the canvas
|
||||||
|
await drawMarkers(ctx, markers, z);
|
||||||
|
|
||||||
|
return canvas.toBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderWatermark = (width, height, scale, text) => {
|
||||||
|
const canvas = createCanvas(scale * width, scale * height);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
|
||||||
|
ctx.font = '10px sans-serif';
|
||||||
|
ctx.strokeWidth = '1px';
|
||||||
|
ctx.strokeStyle = 'rgba(255,255,255,.4)';
|
||||||
|
ctx.strokeText(text, 5, height - 5);
|
||||||
|
ctx.fillStyle = 'rgba(0,0,0,.4)';
|
||||||
|
ctx.fillText(text, 5, height - 5);
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderAttribution = (width, height, scale, text) => {
|
||||||
|
const canvas = createCanvas(scale * width, scale * height);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
|
||||||
|
ctx.font = '10px sans-serif';
|
||||||
|
const textMetrics = ctx.measureText(text);
|
||||||
|
const textWidth = textMetrics.width;
|
||||||
|
const textHeight = 14;
|
||||||
|
|
||||||
|
const padding = 6;
|
||||||
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
|
||||||
|
ctx.fillRect(
|
||||||
|
width - textWidth - padding,
|
||||||
|
height - textHeight - padding,
|
||||||
|
textWidth + padding,
|
||||||
|
textHeight + padding,
|
||||||
|
);
|
||||||
|
ctx.fillStyle = 'rgba(0,0,0,.8)';
|
||||||
|
ctx.fillText(text, width - textWidth - padding / 2, height - textHeight + 8);
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
};
|
|
@ -1,10 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import fs from 'node:fs';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import { getFontsPbf } from './utils.js';
|
import { getFontsPbf, listFonts } from './utils.js';
|
||||||
|
|
||||||
export const serve_font = (options, allowedFonts) => {
|
export const serve_font = (options, allowedFonts) => {
|
||||||
const app = express().disable('x-powered-by');
|
const app = express().disable('x-powered-by');
|
||||||
|
@ -14,29 +12,6 @@ export const serve_font = (options, allowedFonts) => {
|
||||||
const fontPath = options.paths.fonts;
|
const fontPath = options.paths.fonts;
|
||||||
|
|
||||||
const existingFonts = {};
|
const existingFonts = {};
|
||||||
const fontListingPromise = new Promise((resolve, reject) => {
|
|
||||||
fs.readdir(options.paths.fonts, (err, files) => {
|
|
||||||
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() &&
|
|
||||||
fs.existsSync(path.join(fontPath, file, '0-255.pbf'))
|
|
||||||
) {
|
|
||||||
existingFonts[path.basename(file)] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/fonts/:fontstack/:range([\\d]+-[\\d]+).pbf', (req, res, next) => {
|
app.get('/fonts/:fontstack/:range([\\d]+-[\\d]+).pbf', (req, res, next) => {
|
||||||
const fontstack = decodeURI(req.params.fontstack);
|
const fontstack = decodeURI(req.params.fontstack);
|
||||||
|
@ -65,5 +40,8 @@ export const serve_font = (options, allowedFonts) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return fontListingPromise.then(() => app);
|
return listFonts(options.paths.fonts).then((fonts) => {
|
||||||
|
Object.assign(existingFonts, fonts);
|
||||||
|
return app;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ import path from 'path';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
import { createCanvas, Image } from 'canvas';
|
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
||||||
import sharp from 'sharp'; // sharp has to be required before node-canvas on linux but after it on windows. see https://github.com/lovell/sharp/issues/371
|
import sharp from 'sharp'; // sharp has to be required before node-canvas on linux but after it on windows. see https://github.com/lovell/sharp/issues/371
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
|
@ -20,6 +20,7 @@ import proj4 from 'proj4';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import {
|
import {
|
||||||
getFontsPbf,
|
getFontsPbf,
|
||||||
|
listFonts,
|
||||||
getTileUrls,
|
getTileUrls,
|
||||||
isValidHttpUrl,
|
isValidHttpUrl,
|
||||||
fixTileJSONCenter,
|
fixTileJSONCenter,
|
||||||
|
@ -330,263 +331,6 @@ const extractMarkersFromQuery = (query, options, transformer) => {
|
||||||
return markers;
|
return markers;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms coordinates to pixels.
|
|
||||||
* @param {List[Number]} ll Longitude/Latitude coordinate pair.
|
|
||||||
* @param {number} zoom Map zoom level.
|
|
||||||
*/
|
|
||||||
const precisePx = (ll, zoom) => {
|
|
||||||
const px = mercator.px(ll, 20);
|
|
||||||
const scale = Math.pow(2, zoom - 20);
|
|
||||||
return [px[0] * scale, px[1] * scale];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a marker in cavans context.
|
|
||||||
* @param {object} ctx Canvas context object.
|
|
||||||
* @param {object} marker Marker object parsed by extractMarkersFromQuery.
|
|
||||||
* @param {number} z Map zoom level.
|
|
||||||
*/
|
|
||||||
const drawMarker = (ctx, marker, z) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const img = new Image();
|
|
||||||
const pixelCoords = precisePx(marker.location, z);
|
|
||||||
|
|
||||||
const getMarkerCoordinates = (imageWidth, imageHeight, scale) => {
|
|
||||||
// Images are placed with their top-left corner at the provided location
|
|
||||||
// within the canvas but we expect icons to be centered and above it.
|
|
||||||
|
|
||||||
// Substract half of the images width from the x-coordinate to center
|
|
||||||
// the image in relation to the provided location
|
|
||||||
let xCoordinate = pixelCoords[0] - imageWidth / 2;
|
|
||||||
// Substract the images height from the y-coordinate to place it above
|
|
||||||
// the provided location
|
|
||||||
let yCoordinate = pixelCoords[1] - imageHeight;
|
|
||||||
|
|
||||||
// Since image placement is dependent on the size offsets have to be
|
|
||||||
// scaled as well. Additionally offsets are provided as either positive or
|
|
||||||
// negative values so we always add them
|
|
||||||
if (marker.offsetX) {
|
|
||||||
xCoordinate = xCoordinate + marker.offsetX * scale;
|
|
||||||
}
|
|
||||||
if (marker.offsetY) {
|
|
||||||
yCoordinate = yCoordinate + marker.offsetY * scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: xCoordinate,
|
|
||||||
y: yCoordinate,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawOnCanvas = () => {
|
|
||||||
// Check if the images should be resized before beeing drawn
|
|
||||||
const defaultScale = 1;
|
|
||||||
const scale = marker.scale ? marker.scale : defaultScale;
|
|
||||||
|
|
||||||
// Calculate scaled image sizes
|
|
||||||
const imageWidth = img.width * scale;
|
|
||||||
const imageHeight = img.height * scale;
|
|
||||||
|
|
||||||
// Pass the desired sizes to get correlating coordinates
|
|
||||||
const coords = getMarkerCoordinates(imageWidth, imageHeight, scale);
|
|
||||||
|
|
||||||
// Draw the image on canvas
|
|
||||||
if (scale != defaultScale) {
|
|
||||||
ctx.drawImage(img, coords.x, coords.y, imageWidth, imageHeight);
|
|
||||||
} else {
|
|
||||||
ctx.drawImage(img, coords.x, coords.y);
|
|
||||||
}
|
|
||||||
// Resolve the promise when image has been drawn
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
img.onload = drawOnCanvas;
|
|
||||||
img.onerror = (err) => {
|
|
||||||
throw err;
|
|
||||||
};
|
|
||||||
img.src = marker.icon;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a list of markers onto a canvas.
|
|
||||||
* Wraps drawing of markers into list of promises and awaits them.
|
|
||||||
* It's required because images are expected to load asynchronous in canvas js
|
|
||||||
* even when provided from a local disk.
|
|
||||||
* @param {object} ctx Canvas context object.
|
|
||||||
* @param {List[Object]} markers Marker objects parsed by extractMarkersFromQuery.
|
|
||||||
* @param {number} z Map zoom level.
|
|
||||||
*/
|
|
||||||
const drawMarkers = async (ctx, markers, z) => {
|
|
||||||
const markerPromises = [];
|
|
||||||
|
|
||||||
for (const marker of markers) {
|
|
||||||
// Begin drawing marker
|
|
||||||
markerPromises.push(drawMarker(ctx, marker, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Await marker drawings before continuing
|
|
||||||
await Promise.all(markerPromises);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a list of coordinates onto a canvas and styles the resulting path.
|
|
||||||
* @param {object} ctx Canvas context object.
|
|
||||||
* @param {List[Number]} path List of coordinates.
|
|
||||||
* @param {object} query Request query parameters.
|
|
||||||
* @param {string} pathQuery Path query parameter.
|
|
||||||
* @param {number} z Map zoom level.
|
|
||||||
*/
|
|
||||||
const drawPath = (ctx, path, query, pathQuery, z) => {
|
|
||||||
const splitPaths = pathQuery.split('|');
|
|
||||||
|
|
||||||
if (!path || path.length < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
|
|
||||||
// Transform coordinates to pixel on canvas and draw lines between points
|
|
||||||
for (const pair of path) {
|
|
||||||
const px = precisePx(pair, z);
|
|
||||||
ctx.lineTo(px[0], px[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if first coordinate matches last coordinate
|
|
||||||
if (
|
|
||||||
path[0][0] === path[path.length - 1][0] &&
|
|
||||||
path[0][1] === path[path.length - 1][1]
|
|
||||||
) {
|
|
||||||
ctx.closePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optionally fill drawn shape with a rgba color from query
|
|
||||||
const pathHasFill = splitPaths.filter((x) => x.startsWith('fill')).length > 0;
|
|
||||||
if (query.fill !== undefined || pathHasFill) {
|
|
||||||
if ('fill' in query) {
|
|
||||||
ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
|
|
||||||
}
|
|
||||||
if (pathHasFill) {
|
|
||||||
ctx.fillStyle = splitPaths
|
|
||||||
.find((x) => x.startsWith('fill:'))
|
|
||||||
.replace('fill:', '');
|
|
||||||
}
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get line width from query and fall back to 1 if not provided
|
|
||||||
const pathHasWidth =
|
|
||||||
splitPaths.filter((x) => x.startsWith('width')).length > 0;
|
|
||||||
if (query.width !== undefined || pathHasWidth) {
|
|
||||||
let lineWidth = 1;
|
|
||||||
// Get line width from query
|
|
||||||
if ('width' in query) {
|
|
||||||
lineWidth = Number(query.width);
|
|
||||||
}
|
|
||||||
// Get line width from path in query
|
|
||||||
if (pathHasWidth) {
|
|
||||||
lineWidth = Number(
|
|
||||||
splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Get border width from query and fall back to 10% of line width
|
|
||||||
const borderWidth =
|
|
||||||
query.borderwidth !== undefined
|
|
||||||
? parseFloat(query.borderwidth)
|
|
||||||
: lineWidth * 0.1;
|
|
||||||
|
|
||||||
// Set rendering style for the start and end points of the path
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
|
||||||
ctx.lineCap = query.linecap || 'butt';
|
|
||||||
|
|
||||||
// Set rendering style for overlapping segments of the path with differing directions
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
|
||||||
ctx.lineJoin = query.linejoin || 'miter';
|
|
||||||
|
|
||||||
// In order to simulate a border we draw the path two times with the first
|
|
||||||
// beeing the wider border part.
|
|
||||||
if (query.border !== undefined && borderWidth > 0) {
|
|
||||||
// We need to double the desired border width and add it to the line width
|
|
||||||
// in order to get the desired border on each side of the line.
|
|
||||||
ctx.lineWidth = lineWidth + borderWidth * 2;
|
|
||||||
// Set border style as rgba
|
|
||||||
ctx.strokeStyle = query.border;
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathHasStroke =
|
|
||||||
splitPaths.filter((x) => x.startsWith('stroke')).length > 0;
|
|
||||||
if (query.stroke !== undefined || pathHasStroke) {
|
|
||||||
if ('stroke' in query) {
|
|
||||||
ctx.strokeStyle = query.stroke;
|
|
||||||
}
|
|
||||||
// Path Stroke gets higher priority
|
|
||||||
if (pathHasStroke) {
|
|
||||||
ctx.strokeStyle = splitPaths
|
|
||||||
.find((x) => x.startsWith('stroke:'))
|
|
||||||
.replace('stroke:', '');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderOverlay = async (
|
|
||||||
z,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
paths,
|
|
||||||
markers,
|
|
||||||
query,
|
|
||||||
) => {
|
|
||||||
if ((!paths || paths.length === 0) && (!markers || markers.length === 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const center = precisePx([x, y], z);
|
|
||||||
|
|
||||||
const mapHeight = 512 * (1 << z);
|
|
||||||
const maxEdge = center[1] + h / 2;
|
|
||||||
const minEdge = center[1] - h / 2;
|
|
||||||
if (maxEdge > mapHeight) {
|
|
||||||
center[1] -= maxEdge - mapHeight;
|
|
||||||
} else if (minEdge < 0) {
|
|
||||||
center[1] -= minEdge;
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvas = createCanvas(scale * w, scale * h);
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx.scale(scale, scale);
|
|
||||||
if (bearing) {
|
|
||||||
ctx.translate(w / 2, h / 2);
|
|
||||||
ctx.rotate((-bearing / 180) * Math.PI);
|
|
||||||
ctx.translate(-center[0], -center[1]);
|
|
||||||
} else {
|
|
||||||
// optimized path
|
|
||||||
ctx.translate(-center[0] + w / 2, -center[1] + h / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw provided paths if any
|
|
||||||
paths.forEach((path, i) => {
|
|
||||||
const pathQuery = Array.isArray(query.path) ? query.path.at(i) : query.path;
|
|
||||||
drawPath(ctx, path, query, pathQuery, z);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Await drawing of markers before rendering the canvas
|
|
||||||
await drawMarkers(ctx, markers, z);
|
|
||||||
|
|
||||||
return canvas.toBuffer();
|
|
||||||
};
|
|
||||||
|
|
||||||
const calcZForBBox = (bbox, w, h, query) => {
|
const calcZForBBox = (bbox, w, h, query) => {
|
||||||
let z = 25;
|
let z = 25;
|
||||||
|
|
||||||
|
@ -608,32 +352,179 @@ const calcZForBBox = (bbox, w, h, query) => {
|
||||||
return z;
|
return z;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const respondImage = (
|
||||||
|
options,
|
||||||
|
item,
|
||||||
|
z,
|
||||||
|
lon,
|
||||||
|
lat,
|
||||||
|
bearing,
|
||||||
|
pitch,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
scale,
|
||||||
|
format,
|
||||||
|
res,
|
||||||
|
overlay = null,
|
||||||
|
mode = 'tile',
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
Math.abs(lon) > 180 ||
|
||||||
|
Math.abs(lat) > 85.06 ||
|
||||||
|
lon !== lon ||
|
||||||
|
lat !== lat
|
||||||
|
) {
|
||||||
|
return res.status(400).send('Invalid center');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Math.min(width, height) <= 0 ||
|
||||||
|
Math.max(width, height) * scale > (options.maxSize || 2048) ||
|
||||||
|
width !== width ||
|
||||||
|
height !== height
|
||||||
|
) {
|
||||||
|
return res.status(400).send('Invalid size');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format === 'png' || format === 'webp') {
|
||||||
|
} else if (format === 'jpg' || format === 'jpeg') {
|
||||||
|
format = 'jpeg';
|
||||||
|
} else {
|
||||||
|
return res.status(400).send('Invalid format');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tileMargin = Math.max(options.tileMargin || 0, 0);
|
||||||
|
let pool;
|
||||||
|
if (mode === 'tile' && tileMargin === 0) {
|
||||||
|
pool = item.map.renderers[scale];
|
||||||
|
} else {
|
||||||
|
pool = item.map.renderers_static[scale];
|
||||||
|
}
|
||||||
|
pool.acquire((err, renderer) => {
|
||||||
|
const mlglZ = Math.max(0, z - 1);
|
||||||
|
const params = {
|
||||||
|
zoom: mlglZ,
|
||||||
|
center: [lon, lat],
|
||||||
|
bearing: bearing,
|
||||||
|
pitch: pitch,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (z === 0) {
|
||||||
|
params.width *= 2;
|
||||||
|
params.height *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z > 2 && tileMargin > 0) {
|
||||||
|
params.width += tileMargin * 2;
|
||||||
|
params.height += tileMargin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.render(params, (err, data) => {
|
||||||
|
pool.release(renderer);
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).header('Content-Type', 'text/plain').send(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix semi-transparent outlines on raw, premultiplied input
|
||||||
|
// https://github.com/maptiler/tileserver-gl/issues/350#issuecomment-477857040
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const alpha = data[i + 3];
|
||||||
|
const norm = alpha / 255;
|
||||||
|
if (alpha === 0) {
|
||||||
|
data[i] = 0;
|
||||||
|
data[i + 1] = 0;
|
||||||
|
data[i + 2] = 0;
|
||||||
|
} else {
|
||||||
|
data[i] = data[i] / norm;
|
||||||
|
data[i + 1] = data[i + 1] / norm;
|
||||||
|
data[i + 2] = data[i + 2] / norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = sharp(data, {
|
||||||
|
raw: {
|
||||||
|
width: params.width * scale,
|
||||||
|
height: params.height * scale,
|
||||||
|
channels: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (z > 2 && tileMargin > 0) {
|
||||||
|
const [_, y] = mercator.px(params.center, z);
|
||||||
|
let yoffset = Math.max(
|
||||||
|
Math.min(0, y - 128 - tileMargin),
|
||||||
|
y + 128 + tileMargin - Math.pow(2, z + 8),
|
||||||
|
);
|
||||||
|
image.extract({
|
||||||
|
left: tileMargin * scale,
|
||||||
|
top: (tileMargin + yoffset) * scale,
|
||||||
|
width: width * scale,
|
||||||
|
height: height * scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z === 0) {
|
||||||
|
// HACK: when serving zoom 0, resize the 0 tile from 512 to 256
|
||||||
|
image.resize(width * scale, height * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
const composite_array = [];
|
||||||
|
if (overlay) {
|
||||||
|
composite_array.push({ input: overlay });
|
||||||
|
}
|
||||||
|
if (item.watermark) {
|
||||||
|
const canvas = renderWatermark(width, height, scale, item.watermark);
|
||||||
|
|
||||||
|
composite_array.push({ input: canvas.toBuffer() });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'static' && item.staticAttributionText) {
|
||||||
|
const canvas = renderAttribution(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
scale,
|
||||||
|
item.staticAttributionText,
|
||||||
|
);
|
||||||
|
|
||||||
|
composite_array.push({ input: canvas.toBuffer() });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (composite_array.length > 0) {
|
||||||
|
image.composite(composite_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatQuality = (options.formatQuality || {})[format];
|
||||||
|
|
||||||
|
if (format === 'png') {
|
||||||
|
image.png({ adaptiveFiltering: false });
|
||||||
|
} else if (format === 'jpeg') {
|
||||||
|
image.jpeg({ quality: formatQuality || 80 });
|
||||||
|
} else if (format === 'webp') {
|
||||||
|
image.webp({ quality: formatQuality || 90 });
|
||||||
|
}
|
||||||
|
image.toBuffer((err, buffer, info) => {
|
||||||
|
if (!buffer) {
|
||||||
|
return res.status(404).send('Not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
res.set({
|
||||||
|
'Last-Modified': item.lastModified,
|
||||||
|
'Content-Type': `image/${format}`,
|
||||||
|
});
|
||||||
|
return res.status(200).send(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const existingFonts = {};
|
const existingFonts = {};
|
||||||
let maxScaleFactor = 2;
|
let maxScaleFactor = 2;
|
||||||
|
|
||||||
export const serve_rendered = {
|
export const serve_rendered = {
|
||||||
init: (options, repo) => {
|
init: (options, repo) => {
|
||||||
const fontListingPromise = new Promise((resolve, reject) => {
|
|
||||||
fs.readdir(options.paths.fonts, (err, files) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const file of files) {
|
|
||||||
fs.stat(path.join(options.paths.fonts, file), (err, stats) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stats.isDirectory()) {
|
|
||||||
existingFonts[path.basename(file)] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
|
maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
|
||||||
let scalePattern = '';
|
let scalePattern = '';
|
||||||
for (let i = 2; i <= maxScaleFactor; i++) {
|
for (let i = 2; i <= maxScaleFactor; i++) {
|
||||||
|
@ -643,203 +534,6 @@ export const serve_rendered = {
|
||||||
|
|
||||||
const app = express().disable('x-powered-by');
|
const app = express().disable('x-powered-by');
|
||||||
|
|
||||||
const respondImage = (
|
|
||||||
item,
|
|
||||||
z,
|
|
||||||
lon,
|
|
||||||
lat,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
scale,
|
|
||||||
format,
|
|
||||||
res,
|
|
||||||
next,
|
|
||||||
opt_overlay,
|
|
||||||
opt_mode = 'tile',
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
Math.abs(lon) > 180 ||
|
|
||||||
Math.abs(lat) > 85.06 ||
|
|
||||||
lon !== lon ||
|
|
||||||
lat !== lat
|
|
||||||
) {
|
|
||||||
return res.status(400).send('Invalid center');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Math.min(width, height) <= 0 ||
|
|
||||||
Math.max(width, height) * scale > (options.maxSize || 2048) ||
|
|
||||||
width !== width ||
|
|
||||||
height !== height
|
|
||||||
) {
|
|
||||||
return res.status(400).send('Invalid size');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format === 'png' || format === 'webp') {
|
|
||||||
} else if (format === 'jpg' || format === 'jpeg') {
|
|
||||||
format = 'jpeg';
|
|
||||||
} else {
|
|
||||||
return res.status(400).send('Invalid format');
|
|
||||||
}
|
|
||||||
|
|
||||||
const tileMargin = Math.max(options.tileMargin || 0, 0);
|
|
||||||
let pool;
|
|
||||||
if (opt_mode === 'tile' && tileMargin === 0) {
|
|
||||||
pool = item.map.renderers[scale];
|
|
||||||
} else {
|
|
||||||
pool = item.map.renderers_static[scale];
|
|
||||||
}
|
|
||||||
pool.acquire((err, renderer) => {
|
|
||||||
const mlglZ = Math.max(0, z - 1);
|
|
||||||
const params = {
|
|
||||||
zoom: mlglZ,
|
|
||||||
center: [lon, lat],
|
|
||||||
bearing: bearing,
|
|
||||||
pitch: pitch,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (z === 0) {
|
|
||||||
params.width *= 2;
|
|
||||||
params.height *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (z > 2 && tileMargin > 0) {
|
|
||||||
params.width += tileMargin * 2;
|
|
||||||
params.height += tileMargin * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.render(params, (err, data) => {
|
|
||||||
pool.release(renderer);
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res
|
|
||||||
.status(500)
|
|
||||||
.header('Content-Type', 'text/plain')
|
|
||||||
.send(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix semi-transparent outlines on raw, premultiplied input
|
|
||||||
// https://github.com/maptiler/tileserver-gl/issues/350#issuecomment-477857040
|
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
|
||||||
const alpha = data[i + 3];
|
|
||||||
const norm = alpha / 255;
|
|
||||||
if (alpha === 0) {
|
|
||||||
data[i] = 0;
|
|
||||||
data[i + 1] = 0;
|
|
||||||
data[i + 2] = 0;
|
|
||||||
} else {
|
|
||||||
data[i] = data[i] / norm;
|
|
||||||
data[i + 1] = data[i + 1] / norm;
|
|
||||||
data[i + 2] = data[i + 2] / norm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = sharp(data, {
|
|
||||||
raw: {
|
|
||||||
width: params.width * scale,
|
|
||||||
height: params.height * scale,
|
|
||||||
channels: 4,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (z > 2 && tileMargin > 0) {
|
|
||||||
const [_, y] = mercator.px(params.center, z);
|
|
||||||
let yoffset = Math.max(
|
|
||||||
Math.min(0, y - 128 - tileMargin),
|
|
||||||
y + 128 + tileMargin - Math.pow(2, z + 8),
|
|
||||||
);
|
|
||||||
image.extract({
|
|
||||||
left: tileMargin * scale,
|
|
||||||
top: (tileMargin + yoffset) * scale,
|
|
||||||
width: width * scale,
|
|
||||||
height: height * scale,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (z === 0) {
|
|
||||||
// HACK: when serving zoom 0, resize the 0 tile from 512 to 256
|
|
||||||
image.resize(width * scale, height * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
var composite_array = [];
|
|
||||||
if (opt_overlay) {
|
|
||||||
composite_array.push({ input: opt_overlay });
|
|
||||||
}
|
|
||||||
if (item.watermark) {
|
|
||||||
const canvas = createCanvas(scale * width, scale * height);
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx.scale(scale, scale);
|
|
||||||
ctx.font = '10px sans-serif';
|
|
||||||
ctx.strokeWidth = '1px';
|
|
||||||
ctx.strokeStyle = 'rgba(255,255,255,.4)';
|
|
||||||
ctx.strokeText(item.watermark, 5, height - 5);
|
|
||||||
ctx.fillStyle = 'rgba(0,0,0,.4)';
|
|
||||||
ctx.fillText(item.watermark, 5, height - 5);
|
|
||||||
|
|
||||||
composite_array.push({ input: canvas.toBuffer() });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_mode === 'static' && item.staticAttributionText) {
|
|
||||||
const canvas = createCanvas(scale * width, scale * height);
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx.scale(scale, scale);
|
|
||||||
|
|
||||||
ctx.font = '10px sans-serif';
|
|
||||||
const text = item.staticAttributionText;
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const textWidth = textMetrics.width;
|
|
||||||
const textHeight = 14;
|
|
||||||
|
|
||||||
const padding = 6;
|
|
||||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
|
|
||||||
ctx.fillRect(
|
|
||||||
width - textWidth - padding,
|
|
||||||
height - textHeight - padding,
|
|
||||||
textWidth + padding,
|
|
||||||
textHeight + padding,
|
|
||||||
);
|
|
||||||
ctx.fillStyle = 'rgba(0,0,0,.8)';
|
|
||||||
ctx.fillText(
|
|
||||||
item.staticAttributionText,
|
|
||||||
width - textWidth - padding / 2,
|
|
||||||
height - textHeight + 8,
|
|
||||||
);
|
|
||||||
|
|
||||||
composite_array.push({ input: canvas.toBuffer() });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (composite_array.length > 0) {
|
|
||||||
image.composite(composite_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatQuality = (options.formatQuality || {})[format];
|
|
||||||
|
|
||||||
if (format === 'png') {
|
|
||||||
image.png({ adaptiveFiltering: false });
|
|
||||||
} else if (format === 'jpeg') {
|
|
||||||
image.jpeg({ quality: formatQuality || 80 });
|
|
||||||
} else if (format === 'webp') {
|
|
||||||
image.webp({ quality: formatQuality || 90 });
|
|
||||||
}
|
|
||||||
image.toBuffer((err, buffer, info) => {
|
|
||||||
if (!buffer) {
|
|
||||||
return res.status(404).send('Not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set({
|
|
||||||
'Last-Modified': item.lastModified,
|
|
||||||
'Content-Type': `image/${format}`,
|
|
||||||
});
|
|
||||||
return res.status(200).send(buffer);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
app.get(
|
app.get(
|
||||||
`/:id/:z(\\d+)/:x(\\d+)/:y(\\d+):scale(${scalePattern})?.:format([\\w]+)`,
|
`/:id/:z(\\d+)/:x(\\d+)/:y(\\d+):scale(${scalePattern})?.:format([\\w]+)`,
|
||||||
(req, res, next) => {
|
(req, res, next) => {
|
||||||
|
@ -880,6 +574,7 @@ export const serve_rendered = {
|
||||||
z,
|
z,
|
||||||
);
|
);
|
||||||
return respondImage(
|
return respondImage(
|
||||||
|
options,
|
||||||
item,
|
item,
|
||||||
z,
|
z,
|
||||||
tileCenter[0],
|
tileCenter[0],
|
||||||
|
@ -891,7 +586,6 @@ export const serve_rendered = {
|
||||||
scale,
|
scale,
|
||||||
format,
|
format,
|
||||||
res,
|
res,
|
||||||
next,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -962,6 +656,7 @@ export const serve_rendered = {
|
||||||
);
|
);
|
||||||
|
|
||||||
return respondImage(
|
return respondImage(
|
||||||
|
options,
|
||||||
item,
|
item,
|
||||||
z,
|
z,
|
||||||
x,
|
x,
|
||||||
|
@ -973,7 +668,6 @@ export const serve_rendered = {
|
||||||
scale,
|
scale,
|
||||||
format,
|
format,
|
||||||
res,
|
res,
|
||||||
next,
|
|
||||||
overlay,
|
overlay,
|
||||||
'static',
|
'static',
|
||||||
);
|
);
|
||||||
|
@ -1043,6 +737,7 @@ export const serve_rendered = {
|
||||||
req.query,
|
req.query,
|
||||||
);
|
);
|
||||||
return respondImage(
|
return respondImage(
|
||||||
|
options,
|
||||||
item,
|
item,
|
||||||
z,
|
z,
|
||||||
x,
|
x,
|
||||||
|
@ -1054,7 +749,6 @@ export const serve_rendered = {
|
||||||
scale,
|
scale,
|
||||||
format,
|
format,
|
||||||
res,
|
res,
|
||||||
next,
|
|
||||||
overlay,
|
overlay,
|
||||||
'static',
|
'static',
|
||||||
);
|
);
|
||||||
|
@ -1177,6 +871,7 @@ export const serve_rendered = {
|
||||||
);
|
);
|
||||||
|
|
||||||
return respondImage(
|
return respondImage(
|
||||||
|
options,
|
||||||
item,
|
item,
|
||||||
z,
|
z,
|
||||||
x,
|
x,
|
||||||
|
@ -1188,7 +883,6 @@ export const serve_rendered = {
|
||||||
scale,
|
scale,
|
||||||
format,
|
format,
|
||||||
res,
|
res,
|
||||||
next,
|
|
||||||
overlay,
|
overlay,
|
||||||
'static',
|
'static',
|
||||||
);
|
);
|
||||||
|
@ -1215,7 +909,10 @@ export const serve_rendered = {
|
||||||
return res.send(info);
|
return res.send(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all([fontListingPromise]).then(() => app);
|
return listFonts(options.paths.fonts).then((fonts) => {
|
||||||
|
Object.assign(existingFonts, fonts);
|
||||||
|
return app;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
add: async (options, repo, params, id, publicUrl, dataResolver) => {
|
add: async (options, repo, params, id, publicUrl, dataResolver) => {
|
||||||
const map = {
|
const map = {
|
||||||
|
@ -1618,7 +1315,7 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all([renderersReadyPromise]);
|
return renderersReadyPromise;
|
||||||
},
|
},
|
||||||
remove: (repo, id) => {
|
remove: (repo, id) => {
|
||||||
const item = repo[id];
|
const item = repo[id];
|
||||||
|
|
|
@ -98,9 +98,7 @@ export const serve_style = {
|
||||||
|
|
||||||
const validationErrors = validate(styleFileData);
|
const validationErrors = validate(styleFileData);
|
||||||
if (validationErrors.length > 0) {
|
if (validationErrors.length > 0) {
|
||||||
console.log(
|
console.log(`The file "${params.style}" is not a valid style file:`);
|
||||||
`The file "${params.style}" is not valid a valid style file:`,
|
|
||||||
);
|
|
||||||
for (const err of validationErrors) {
|
for (const err of validationErrors) {
|
||||||
console.log(`${err.line}: ${err.message}`);
|
console.log(`${err.line}: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|
29
src/utils.js
29
src/utils.js
|
@ -163,6 +163,35 @@ export const getFontsPbf = (
|
||||||
return Promise.all(queue).then((values) => glyphCompose.combine(values));
|
return Promise.all(queue).then((values) => glyphCompose.combine(values));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const listFonts = async (fontPath) => {
|
||||||
|
const existingFonts = {};
|
||||||
|
const fontListingPromise = new Promise((resolve, reject) => {
|
||||||
|
fs.readdir(fontPath, (err, files) => {
|
||||||
|
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() &&
|
||||||
|
fs.existsSync(path.join(fontPath, file, '0-255.pbf'))
|
||||||
|
) {
|
||||||
|
existingFonts[path.basename(file)] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await fontListingPromise;
|
||||||
|
return existingFonts;
|
||||||
|
};
|
||||||
|
|
||||||
export const isValidHttpUrl = (string) => {
|
export const isValidHttpUrl = (string) => {
|
||||||
let url;
|
let url;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,5 @@ after(function () {
|
||||||
console.log('global teardown');
|
console.log('global teardown');
|
||||||
global.server.close(function () {
|
global.server.close(function () {
|
||||||
console.log('Done');
|
console.log('Done');
|
||||||
process.exit();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue