tileserver-gl-light 4.4.0 โ 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -0
- package/.github/workflows/automerger.yml +1 -5
- package/.github/workflows/ci.yml +51 -0
- package/.github/workflows/codeql.yml +2 -0
- package/.github/workflows/ct.yml +6 -8
- package/.github/workflows/pipeline.yml +43 -0
- package/.github/workflows/release.yml +11 -5
- package/.hadolint.yml +3 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/docs/endpoints.rst +2 -0
- package/package.json +6 -5
- package/src/serve_rendered.js +153 -86
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
public
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
name: 'Auto Merge PRs'
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
5
|
-
branches:
|
|
6
|
-
- master
|
|
4
|
+
workflow_call:
|
|
7
5
|
|
|
8
6
|
permissions:
|
|
9
7
|
pull-requests: write
|
|
@@ -12,8 +10,6 @@ permissions:
|
|
|
12
10
|
jobs:
|
|
13
11
|
automerge:
|
|
14
12
|
runs-on: ubuntu-latest
|
|
15
|
-
if: >
|
|
16
|
-
github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'
|
|
17
13
|
steps:
|
|
18
14
|
- uses: fastify/github-action-merge-dependabot@v3
|
|
19
15
|
with:
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: 'Continuous Integration'
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
checks: write
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
ci:
|
|
12
|
+
runs-on: ubuntu-20.04
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check out repository โจ (non-dependabot)
|
|
15
|
+
if: ${{ github.actor != 'dependabot[bot]' }}
|
|
16
|
+
uses: actions/checkout@v3
|
|
17
|
+
|
|
18
|
+
- name: Check out repository ๐ (dependabot)
|
|
19
|
+
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
20
|
+
uses: actions/checkout@v3
|
|
21
|
+
with:
|
|
22
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
23
|
+
|
|
24
|
+
- name: Setup node env ๐ฆ
|
|
25
|
+
uses: actions/setup-node@v3
|
|
26
|
+
with:
|
|
27
|
+
node-version-file: 'package.json'
|
|
28
|
+
check-latest: true
|
|
29
|
+
cache: 'npm'
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies ๐
|
|
32
|
+
run: npm ci --prefer-offline --no-audit --omit=optional
|
|
33
|
+
|
|
34
|
+
- name: Run linter(s) ๐
|
|
35
|
+
uses: wearerequired/lint-action@v2
|
|
36
|
+
with:
|
|
37
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
38
|
+
continue_on_error: false
|
|
39
|
+
git_name: github-actions[bot]
|
|
40
|
+
git_email: github-actions[bot]@users.noreply.github.com
|
|
41
|
+
auto_fix: false
|
|
42
|
+
eslint: true
|
|
43
|
+
eslint_extensions: js,cjs,mjs,ts
|
|
44
|
+
prettier: true
|
|
45
|
+
prettier_extensions: js,cjs,ts,json
|
|
46
|
+
|
|
47
|
+
- name: Run hadolint ๐ณ
|
|
48
|
+
uses: hadolint/hadolint-action@v3.0.0
|
|
49
|
+
with:
|
|
50
|
+
dockerfile: Dockerfile
|
|
51
|
+
ignore: DL3008,DL3015
|
package/.github/workflows/ct.yml
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
name: 'Continuous Testing'
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
5
|
-
branches:
|
|
6
|
-
- master
|
|
7
|
-
pull_request:
|
|
8
|
-
branches:
|
|
9
|
-
- master
|
|
4
|
+
workflow_call:
|
|
10
5
|
|
|
11
6
|
permissions:
|
|
12
7
|
checks: write
|
|
@@ -55,7 +50,7 @@ jobs:
|
|
|
55
50
|
|
|
56
51
|
- name: Run tests ๐งช
|
|
57
52
|
run: xvfb-run --server-args="-screen 0 1024x768x24" npm test
|
|
58
|
-
|
|
53
|
+
|
|
59
54
|
- name: Set up QEMU
|
|
60
55
|
uses: docker/setup-qemu-action@v2
|
|
61
56
|
with:
|
|
@@ -63,10 +58,13 @@ jobs:
|
|
|
63
58
|
|
|
64
59
|
- name: Set up Docker Buildx
|
|
65
60
|
uses: docker/setup-buildx-action@v2
|
|
66
|
-
|
|
61
|
+
|
|
67
62
|
- name: Test Docker Build
|
|
68
63
|
uses: docker/build-push-action@v3
|
|
69
64
|
with:
|
|
70
65
|
context: .
|
|
71
66
|
push: false
|
|
72
67
|
platforms: linux/arm64,linux/amd64
|
|
68
|
+
# experimental: https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#cache-backend-api
|
|
69
|
+
cache-from: type=gha
|
|
70
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: 'The Pipeline'
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- master
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.ref }}-1
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
extract-branch:
|
|
17
|
+
name: 'Fetch branch'
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
outputs:
|
|
20
|
+
current_branch: ${{ steps.get-branch.outputs.current_branch }}
|
|
21
|
+
steps:
|
|
22
|
+
- name: Extract branch name ๐
|
|
23
|
+
id: get-branch
|
|
24
|
+
run: echo "current_branch=${GITHUB_REF##*/}" >> $GITHUB_OUTPUT
|
|
25
|
+
ci:
|
|
26
|
+
name: 'CI'
|
|
27
|
+
needs:
|
|
28
|
+
- extract-branch
|
|
29
|
+
uses: maptiler/tileserver-gl/.github/workflows/ci.yml@master
|
|
30
|
+
ct:
|
|
31
|
+
name: 'CT'
|
|
32
|
+
needs:
|
|
33
|
+
- extract-branch
|
|
34
|
+
uses: maptiler/tileserver-gl/.github/workflows/ct.yml@master
|
|
35
|
+
automerger:
|
|
36
|
+
name: 'Automerge Dependabot PRs'
|
|
37
|
+
needs:
|
|
38
|
+
- ci
|
|
39
|
+
- ct
|
|
40
|
+
- extract-branch
|
|
41
|
+
if: >
|
|
42
|
+
github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'
|
|
43
|
+
uses: maptiler/tileserver-gl/.github/workflows/automerger.yml@master
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: 'Build, Test, Release'
|
|
2
2
|
|
|
3
|
-
on:
|
|
3
|
+
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
5
|
inputs:
|
|
6
6
|
docker_user:
|
|
7
|
-
description: 'Docker Username'
|
|
7
|
+
description: 'Docker Username'
|
|
8
8
|
required: true
|
|
9
9
|
docker_token:
|
|
10
|
-
description: 'Docker Token'
|
|
10
|
+
description: 'Docker Token'
|
|
11
11
|
required: true
|
|
12
12
|
npm_token:
|
|
13
13
|
description: 'NPM Token'
|
|
@@ -15,7 +15,7 @@ on:
|
|
|
15
15
|
|
|
16
16
|
jobs:
|
|
17
17
|
release:
|
|
18
|
-
name:
|
|
18
|
+
name: 'Build, Test, Publish'
|
|
19
19
|
runs-on: ubuntu-20.04
|
|
20
20
|
steps:
|
|
21
21
|
- name: Check out repository โจ
|
|
@@ -86,6 +86,9 @@ jobs:
|
|
|
86
86
|
push: true
|
|
87
87
|
tags: maptiler/tileserver-gl:latest, maptiler/tileserver-gl:v${{ env.PACKAGE_VERSION }}
|
|
88
88
|
platforms: linux/arm64,linux/amd64
|
|
89
|
+
# experimental: https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#cache-backend-api
|
|
90
|
+
cache-from: type=gha
|
|
91
|
+
cache-to: type=gha,mode=max
|
|
89
92
|
|
|
90
93
|
- name: Create Tileserver Light Directory
|
|
91
94
|
run: node publish.js --no-publish
|
|
@@ -110,3 +113,6 @@ jobs:
|
|
|
110
113
|
push: true
|
|
111
114
|
tags: maptiler/tileserver-gl-light:latest, maptiler/tileserver-gl-light:v${{ env.PACKAGE_VERSION }}
|
|
112
115
|
platforms: linux/arm64,linux/amd64
|
|
116
|
+
# experimental: https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#cache-backend-api
|
|
117
|
+
cache-from: type=gha
|
|
118
|
+
cache-to: type=gha,mode=max
|
package/.hadolint.yml
ADDED
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
18
|
package/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
public
|
package/docs/endpoints.rst
CHANGED
|
@@ -39,6 +39,8 @@ Static images
|
|
|
39
39
|
|
|
40
40
|
* e.g. ``5.9,45.8|5.9,47.8|10.5,47.8|10.5,45.8|5.9,45.8``
|
|
41
41
|
* can be provided multiple times
|
|
42
|
+
* or pass the path as per [Maptiler Cloud API](https://docs.maptiler.com/cloud/api/static-maps/)
|
|
43
|
+
* Match pattern: ((fill|stroke|width)\:[^\|]+\|)*((enc:.+)|((-?\d+\.?\d*,-?\d+\.?\d*\|)+(-?\d+\.?\d*,-?\d+\.?\d*)))
|
|
42
44
|
|
|
43
45
|
* ``latlng`` - indicates coordinates are in ``lat,lng`` order rather than the usual ``lng,lat``
|
|
44
46
|
* ``fill`` - color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tileserver-gl-light",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.1",
|
|
4
4
|
"description": "Map tile server for JSON GL styles - serving vector tiles",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": "src/main.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@mapbox/glyph-pbf-composite": "0.0.3",
|
|
21
21
|
"@mapbox/mbtiles": "0.12.1",
|
|
22
|
+
"@mapbox/polyline": "^1.1.1",
|
|
22
23
|
"@mapbox/sphericalmercator": "1.2.0",
|
|
23
24
|
"@mapbox/vector-tile": "1.3.1",
|
|
24
25
|
"@maplibre/maplibre-gl-style-spec": "17.0.2",
|
|
@@ -26,17 +27,17 @@
|
|
|
26
27
|
"chokidar": "3.5.3",
|
|
27
28
|
"clone": "2.1.2",
|
|
28
29
|
"color": "4.2.3",
|
|
29
|
-
"commander": "
|
|
30
|
+
"commander": "10.0.0",
|
|
30
31
|
"cors": "2.8.5",
|
|
31
32
|
"express": "4.18.2",
|
|
32
33
|
"handlebars": "4.7.7",
|
|
33
34
|
"http-shutdown": "1.2.2",
|
|
34
35
|
"morgan": "1.10.0",
|
|
35
36
|
"pbf": "3.2.1",
|
|
36
|
-
"proj4": "2.8.
|
|
37
|
+
"proj4": "2.8.1",
|
|
37
38
|
"request": "2.88.2",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
39
|
+
"sanitize-filename": "1.6.3",
|
|
40
|
+
"tileserver-gl-styles": "2.0.0"
|
|
40
41
|
},
|
|
41
42
|
"keywords": [
|
|
42
43
|
"maptiler",
|
package/src/serve_rendered.js
CHANGED
|
@@ -15,11 +15,14 @@ import sanitize from 'sanitize-filename';
|
|
|
15
15
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
|
16
16
|
import mlgl from '@maplibre/maplibre-gl-native';
|
|
17
17
|
import MBTiles from '@mapbox/mbtiles';
|
|
18
|
+
import polyline from '@mapbox/polyline';
|
|
18
19
|
import proj4 from 'proj4';
|
|
19
20
|
import request from 'request';
|
|
20
21
|
import { getFontsPbf, getTileUrls, fixTileJSONCenter } from './utils.js';
|
|
21
22
|
|
|
22
23
|
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
|
24
|
+
const PATH_PATTERN =
|
|
25
|
+
/^((fill|stroke|width)\:[^\|]+\|)*((enc:.+)|((-?\d+\.?\d*,-?\d+\.?\d*\|)+(-?\d+\.?\d*,-?\d+\.?\d*)))/;
|
|
23
26
|
const httpTester = /^(http(s)?:)?\/\//;
|
|
24
27
|
|
|
25
28
|
const mercator = new SphericalMercator();
|
|
@@ -147,47 +150,67 @@ const parseCoordinates = (coordinatePair, query, transformer) => {
|
|
|
147
150
|
* @param {Function} transformer Optional transform function.
|
|
148
151
|
*/
|
|
149
152
|
const extractPathsFromQuery = (query, transformer) => {
|
|
153
|
+
// Initiate paths array
|
|
154
|
+
const paths = [];
|
|
150
155
|
// Return an empty list if no paths have been provided
|
|
151
|
-
if (!query.path) {
|
|
152
|
-
return
|
|
156
|
+
if ('path' in query && !query.path) {
|
|
157
|
+
return paths;
|
|
153
158
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (pair === null) {
|
|
179
|
-
continue;
|
|
159
|
+
// Parse paths provided via path query argument
|
|
160
|
+
if ('path' in query) {
|
|
161
|
+
const providedPaths = Array.isArray(query.path) ? query.path : [query.path];
|
|
162
|
+
// Iterate through paths, parse and validate them
|
|
163
|
+
for (const providedPath of providedPaths) {
|
|
164
|
+
// Logic for pushing coords to path when path includes google polyline
|
|
165
|
+
if (
|
|
166
|
+
providedPath.includes('enc:') &&
|
|
167
|
+
PATH_PATTERN.test(decodeURIComponent(providedPath))
|
|
168
|
+
) {
|
|
169
|
+
const encodedPaths = providedPath.split(',');
|
|
170
|
+
for (const path of encodedPaths) {
|
|
171
|
+
const line = path
|
|
172
|
+
.split('|')
|
|
173
|
+
.filter(
|
|
174
|
+
(x) =>
|
|
175
|
+
!x.startsWith('fill') &&
|
|
176
|
+
!x.startsWith('stroke') &&
|
|
177
|
+
!x.startsWith('width'),
|
|
178
|
+
)
|
|
179
|
+
.join('')
|
|
180
|
+
.replace('enc:', '');
|
|
181
|
+
const coords = polyline.decode(line).map(([lat, lng]) => [lng, lat]);
|
|
182
|
+
paths.push(coords);
|
|
180
183
|
}
|
|
184
|
+
} else {
|
|
185
|
+
// Iterate through paths, parse and validate them
|
|
186
|
+
const currentPath = [];
|
|
187
|
+
|
|
188
|
+
// Extract coordinate-list from path
|
|
189
|
+
const pathParts = (providedPath || '').split('|');
|
|
190
|
+
|
|
191
|
+
// Iterate through coordinate-list, parse the coordinates and validate them
|
|
192
|
+
for (const pair of pathParts) {
|
|
193
|
+
// Extract coordinates from coordinate pair
|
|
194
|
+
const pairParts = pair.split(',');
|
|
195
|
+
// Ensure we have two coordinates
|
|
196
|
+
if (pairParts.length === 2) {
|
|
197
|
+
const pair = parseCoordinates(pairParts, query, transformer);
|
|
198
|
+
|
|
199
|
+
// Ensure coordinates could be parsed and skip them if not
|
|
200
|
+
if (pair === null) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
181
203
|
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
// Add the coordinate-pair to the current path if they are valid
|
|
205
|
+
currentPath.push(pair);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Extend list of paths with current path if it contains coordinates
|
|
209
|
+
if (currentPath.length) {
|
|
210
|
+
paths.push(currentPath);
|
|
211
|
+
}
|
|
184
212
|
}
|
|
185
213
|
}
|
|
186
|
-
|
|
187
|
-
// Extend list of paths with current path if it contains coordinates
|
|
188
|
-
if (currentPath.length) {
|
|
189
|
-
paths.push(currentPath);
|
|
190
|
-
}
|
|
191
214
|
}
|
|
192
215
|
return paths;
|
|
193
216
|
};
|
|
@@ -422,65 +445,109 @@ const drawMarkers = async (ctx, markers, z) => {
|
|
|
422
445
|
* @param {number} z Map zoom level.
|
|
423
446
|
*/
|
|
424
447
|
const drawPath = (ctx, path, query, z) => {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
448
|
+
const renderPath = (splitPaths) => {
|
|
449
|
+
if (!path || path.length < 2) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
428
452
|
|
|
429
|
-
|
|
453
|
+
ctx.beginPath();
|
|
430
454
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
455
|
+
// Transform coordinates to pixel on canvas and draw lines between points
|
|
456
|
+
for (const pair of path) {
|
|
457
|
+
const px = precisePx(pair, z);
|
|
458
|
+
ctx.lineTo(px[0], px[1]);
|
|
459
|
+
}
|
|
436
460
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
461
|
+
// Check if first coordinate matches last coordinate
|
|
462
|
+
if (
|
|
463
|
+
path[0][0] === path[path.length - 1][0] &&
|
|
464
|
+
path[0][1] === path[path.length - 1][1]
|
|
465
|
+
) {
|
|
466
|
+
ctx.closePath();
|
|
467
|
+
}
|
|
444
468
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
469
|
+
// Optionally fill drawn shape with a rgba color from query
|
|
470
|
+
const pathHasFill =
|
|
471
|
+
splitPaths.filter((x) => x.startsWith('fill')).length > 0;
|
|
472
|
+
if (query.fill !== undefined || pathHasFill) {
|
|
473
|
+
if ('fill' in query) {
|
|
474
|
+
ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
|
|
475
|
+
}
|
|
476
|
+
if (pathHasFill) {
|
|
477
|
+
ctx.fillStyle = splitPaths
|
|
478
|
+
.find((x) => x.startsWith('fill:'))
|
|
479
|
+
.replace('fill:', '');
|
|
480
|
+
}
|
|
481
|
+
ctx.fill();
|
|
482
|
+
}
|
|
450
483
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
//
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
// Set
|
|
477
|
-
|
|
478
|
-
ctx.
|
|
484
|
+
// Get line width from query and fall back to 1 if not provided
|
|
485
|
+
const pathHasWidth =
|
|
486
|
+
splitPaths.filter((x) => x.startsWith('width')).length > 0;
|
|
487
|
+
if (query.width !== undefined || pathHasWidth) {
|
|
488
|
+
let lineWidth = 1;
|
|
489
|
+
// Get line width from query
|
|
490
|
+
if ('width' in query) {
|
|
491
|
+
lineWidth = Number(query.width);
|
|
492
|
+
}
|
|
493
|
+
// Get line width from path in query
|
|
494
|
+
if (pathHasWidth) {
|
|
495
|
+
lineWidth = Number(
|
|
496
|
+
splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
// Get border width from query and fall back to 10% of line width
|
|
500
|
+
const borderWidth =
|
|
501
|
+
query.borderwidth !== undefined
|
|
502
|
+
? parseFloat(query.borderwidth)
|
|
503
|
+
: lineWidth * 0.1;
|
|
504
|
+
|
|
505
|
+
// Set rendering style for the start and end points of the path
|
|
506
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
|
507
|
+
ctx.lineCap = query.linecap || 'butt';
|
|
508
|
+
|
|
509
|
+
// Set rendering style for overlapping segments of the path with differing directions
|
|
510
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
|
511
|
+
ctx.lineJoin = query.linejoin || 'miter';
|
|
512
|
+
|
|
513
|
+
// In order to simulate a border we draw the path two times with the first
|
|
514
|
+
// beeing the wider border part.
|
|
515
|
+
if (query.border !== undefined && borderWidth > 0) {
|
|
516
|
+
// We need to double the desired border width and add it to the line width
|
|
517
|
+
// in order to get the desired border on each side of the line.
|
|
518
|
+
ctx.lineWidth = lineWidth + borderWidth * 2;
|
|
519
|
+
// Set border style as rgba
|
|
520
|
+
ctx.strokeStyle = query.border;
|
|
521
|
+
ctx.stroke();
|
|
522
|
+
}
|
|
523
|
+
ctx.lineWidth = lineWidth;
|
|
479
524
|
}
|
|
480
525
|
|
|
481
|
-
|
|
482
|
-
|
|
526
|
+
const pathHasStroke =
|
|
527
|
+
splitPaths.filter((x) => x.startsWith('stroke')).length > 0;
|
|
528
|
+
if (query.stroke !== undefined || pathHasStroke) {
|
|
529
|
+
if ('stroke' in query) {
|
|
530
|
+
ctx.strokeStyle = query.stroke;
|
|
531
|
+
}
|
|
532
|
+
// Path Width gets higher priority
|
|
533
|
+
if (pathHasWidth) {
|
|
534
|
+
ctx.strokeStyle = splitPaths
|
|
535
|
+
.find((x) => x.startsWith('stroke:'))
|
|
536
|
+
.replace('stroke:', '');
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
|
540
|
+
}
|
|
483
541
|
ctx.stroke();
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// Check if path in query is valid
|
|
545
|
+
if (Array.isArray(query.path)) {
|
|
546
|
+
for (let i = 0; i < query.path.length; i += 1) {
|
|
547
|
+
renderPath(decodeURIComponent(query.path.at(i)).split('|'));
|
|
548
|
+
}
|
|
549
|
+
} else {
|
|
550
|
+
renderPath(decodeURIComponent(query.path).split('|'));
|
|
484
551
|
}
|
|
485
552
|
};
|
|
486
553
|
|