@unvt/charites 2.0.3 → 2.1.0
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/dist/cli/serve.js +18 -0
- package/dist/commands/serve.js +28 -22
- package/dist/lib/validate-style.js +2 -2
- package/dist/lib/yaml-writer.js +3 -0
- package/dist/types/index.js +3 -3
- package/dist/types/metadatajson.js +0 -1
- package/dist/types/tilejson.js +0 -1
- package/dist/types/vector_layers.js +0 -1
- package/docs/source/index.rst +1 -1
- package/docs/source/usage/commandline_interface.rst +8 -2
- package/package.json +33 -26
- package/provider/index.html +1 -12
- package/provider/package.json +5 -0
- package/provider/site-env.d.ts +1 -0
- package/provider/{app.css → src/app.css} +1 -1
- package/provider/src/main.ts +137 -0
- package/src/cli/serve.ts +19 -0
- package/src/commands/serve.ts +34 -27
- package/src/lib/tileinfo-importer/base-importer.ts +1 -1
- package/src/lib/validate-style.ts +3 -2
- package/src/lib/yaml-writer.ts +6 -3
- package/src/types/index.ts +3 -3
- package/src/types/tilejson.ts +1 -1
- package/tsconfig.json +10 -2
- package/dist/lib/defaultValues.js +0 -6
- package/provider/app.js +0 -112
- package/src/lib/defaultValues.ts +0 -10
package/dist/cli/serve.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { createServer } from 'vite';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import path from 'path';
|
|
2
5
|
import { serve } from '../commands/serve.js';
|
|
3
6
|
import { error } from '../lib/error.js';
|
|
4
7
|
const program = new Command();
|
|
@@ -9,6 +12,7 @@ program
|
|
|
9
12
|
.option('-i, --sprite-input [<icon input directory>]', 'directory path of icon source to build icons. The default <icon source> is `icons/`')
|
|
10
13
|
.option('--sdf', 'Allows to use SDF sprite in charites')
|
|
11
14
|
.option('--port [port]', 'Specify custom port')
|
|
15
|
+
.option('--vite-port [vitePort]', 'Specify custom port for vite server')
|
|
12
16
|
.option('--no-open', "Don't open the preview in the default browser")
|
|
13
17
|
.action(async (source, serveOptions) => {
|
|
14
18
|
const options = program.opts();
|
|
@@ -16,11 +20,25 @@ program
|
|
|
16
20
|
options.spriteInput = serveOptions.spriteInput;
|
|
17
21
|
options.open = serveOptions.open;
|
|
18
22
|
options.sdf = serveOptions.sdf;
|
|
23
|
+
options.vitePort = serveOptions.vitePort;
|
|
19
24
|
try {
|
|
25
|
+
await startProviderDevServer(options);
|
|
20
26
|
await serve(source, program.opts());
|
|
21
27
|
}
|
|
22
28
|
catch (e) {
|
|
23
29
|
error(e);
|
|
24
30
|
}
|
|
25
31
|
});
|
|
32
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
33
|
+
const __dirname = path.dirname(__filename);
|
|
34
|
+
async function startProviderDevServer(options) {
|
|
35
|
+
const config = {
|
|
36
|
+
root: path.resolve(__dirname, '../../provider'),
|
|
37
|
+
server: {
|
|
38
|
+
port: parseInt(options.vitePort ?? '5137'),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const viteServer = await createServer(config);
|
|
42
|
+
await viteServer.listen();
|
|
43
|
+
}
|
|
26
44
|
export default program;
|
package/dist/commands/serve.js
CHANGED
|
@@ -9,7 +9,6 @@ const WebSocketServer = ws.default.WebSocketServer || ws.WebSocketServer;
|
|
|
9
9
|
import watch from 'node-watch';
|
|
10
10
|
import { parser } from '../lib/yaml-parser.js';
|
|
11
11
|
import { validateStyle } from '../lib/validate-style.js';
|
|
12
|
-
import { providerDir } from '../lib/defaultValues.js';
|
|
13
12
|
import { buildSprite } from '../lib/build-sprite.js';
|
|
14
13
|
export async function serve(source, options) {
|
|
15
14
|
let port = process.env.PORT || 8080;
|
|
@@ -55,12 +54,6 @@ export async function serve(source, options) {
|
|
|
55
54
|
return;
|
|
56
55
|
}
|
|
57
56
|
switch (url) {
|
|
58
|
-
case '/':
|
|
59
|
-
res.statusCode = 200;
|
|
60
|
-
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
|
61
|
-
const content = fs.readFileSync(path.join(providerDir, 'index.html'), 'utf-8');
|
|
62
|
-
res.end(content);
|
|
63
|
-
break;
|
|
64
57
|
case '/style.json':
|
|
65
58
|
let style;
|
|
66
59
|
try {
|
|
@@ -78,24 +71,37 @@ export async function serve(source, options) {
|
|
|
78
71
|
res.setHeader('Cache-Control', 'no-store');
|
|
79
72
|
res.end(JSON.stringify(style));
|
|
80
73
|
break;
|
|
81
|
-
case '/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
res.end(css);
|
|
86
|
-
break;
|
|
87
|
-
case `/app.js`:
|
|
88
|
-
res.statusCode = 200;
|
|
89
|
-
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
|
|
90
|
-
const app = fs.readFileSync(path.join(providerDir, 'app.js'), 'utf-8');
|
|
91
|
-
const js = app.replace('___PORT___', `${port}`);
|
|
92
|
-
res.end(js);
|
|
93
|
-
break;
|
|
94
|
-
default:
|
|
74
|
+
case '/sprite.json':
|
|
75
|
+
case '/sprite@2x.json':
|
|
76
|
+
case '/sprite.png':
|
|
77
|
+
case '/sprite@2x.png':
|
|
95
78
|
res.statusCode = 404;
|
|
96
79
|
res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
|
|
97
80
|
res.end('Not found');
|
|
98
81
|
break;
|
|
82
|
+
default:
|
|
83
|
+
const vitePort = parseInt(options.vitePort || '5137');
|
|
84
|
+
const proxyReq = http.request({
|
|
85
|
+
hostname: 'localhost',
|
|
86
|
+
port: vitePort,
|
|
87
|
+
path: url,
|
|
88
|
+
method: req.method,
|
|
89
|
+
headers: req.headers,
|
|
90
|
+
}, (viteRes) => {
|
|
91
|
+
res.writeHead(viteRes.statusCode || 500, viteRes.headers);
|
|
92
|
+
viteRes.pipe(res, {
|
|
93
|
+
end: true,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
req.pipe(proxyReq, {
|
|
97
|
+
end: true,
|
|
98
|
+
});
|
|
99
|
+
proxyReq.on('error', (err) => {
|
|
100
|
+
console.error('Error with proxy request:', err);
|
|
101
|
+
res.statusCode = 500;
|
|
102
|
+
res.end('Bad gateway: failed to proxy to Vite dev server');
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
99
105
|
}
|
|
100
106
|
});
|
|
101
107
|
server.listen(port, () => {
|
|
@@ -128,7 +134,7 @@ export async function serve(source, options) {
|
|
|
128
134
|
// Nothing to do
|
|
129
135
|
}
|
|
130
136
|
});
|
|
131
|
-
|
|
137
|
+
wss.on('close', () => {
|
|
132
138
|
watcher.close();
|
|
133
139
|
});
|
|
134
140
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { validateStyleMin, } from '@maplibre/maplibre-gl-style-spec';
|
|
2
2
|
export function validateStyle(style) {
|
|
3
|
-
const result =
|
|
3
|
+
const result = validateStyleMin(style);
|
|
4
4
|
const errors = [];
|
|
5
5
|
for (let i = 0; i < result.length; i++) {
|
|
6
6
|
const msg = result[i].message;
|
package/dist/lib/yaml-writer.js
CHANGED
|
@@ -27,9 +27,12 @@ class IncFileTag {
|
|
|
27
27
|
}
|
|
28
28
|
const INC_PATH_TYPE = new YAML.Type('tag:yaml.org,2002:inc/file', {
|
|
29
29
|
kind: 'scalar',
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
31
|
resolve: (data) => data,
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
33
|
construct: (data) => new IncFileTag(data),
|
|
32
34
|
instanceOf: IncFileTag,
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
36
|
represent: (tag) => tag.value,
|
|
34
37
|
});
|
|
35
38
|
const INC_PATH_OUTPUT_SCHEMA = YAML.DEFAULT_SCHEMA.extend([INC_PATH_TYPE]);
|
package/dist/types/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './tilejson';
|
|
2
|
-
export * from './metadatajson';
|
|
3
|
-
export * from './vector_layers';
|
|
1
|
+
export * from './tilejson.js';
|
|
2
|
+
export * from './metadatajson.js';
|
|
3
|
+
export * from './vector_layers.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/tilejson.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/docs/source/index.rst
CHANGED
|
@@ -9,7 +9,7 @@ Charites - Documentation
|
|
|
9
9
|
:Date: 2024-08-23
|
|
10
10
|
:Copyright: CC-BY-SA
|
|
11
11
|
:Organization: The United Nations Vector Tile Toolkit
|
|
12
|
-
:Version: 2.0
|
|
12
|
+
:Version: 2.1.0
|
|
13
13
|
:Abstract: This document contains the complete documentation of Charites, an application to style vector tiles easily
|
|
14
14
|
|
|
15
15
|
.. meta::
|
|
@@ -27,7 +27,7 @@ Checking version
|
|
|
27
27
|
.. code-block:: bash
|
|
28
28
|
|
|
29
29
|
$ charites --version
|
|
30
|
-
|
|
30
|
+
2.1.0
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
Inititalize `style.yml`
|
|
@@ -95,12 +95,18 @@ Realtime editor on browser
|
|
|
95
95
|
source> is `icons/`
|
|
96
96
|
--sdf Allows to use SDF sprite in charites
|
|
97
97
|
--port [port] Specify custom port
|
|
98
|
+
--vite-port [vitePort] Specify custom port for vite server
|
|
99
|
+
--no-open Don't open the preview in the default browser
|
|
98
100
|
-h, --help display help for command
|
|
99
101
|
|
|
100
|
-
Charites has
|
|
102
|
+
Charites has fifth options for `serve` command.
|
|
101
103
|
|
|
102
104
|
- ``--sprite-input`` - If you are building icon spritesheets with Charites, you can specify the directory of SVG files to compile here. See the ``build`` command for more information.
|
|
103
105
|
|
|
104
106
|
- ``--sdf`` - if this option is used together with ``--sprite-input``, the viewer will generate SDF sprite. If the option is not specified, non SDF sprite will be generated.
|
|
105
107
|
|
|
106
108
|
- ``--port`` - Set http server's port number. When not specified, the default is 8080.
|
|
109
|
+
|
|
110
|
+
- ``--vite-port`` - Set Vite server's port number. When not specified, the default is 5137.
|
|
111
|
+
|
|
112
|
+
- ``--no-open`` - If this option is used, the preview will not be opened in the default browser. This is useful when you want to use the preview in a headless environment.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unvt/charites",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"bin": {
|
|
6
6
|
"charites": "dist/cli.js"
|
|
@@ -14,50 +14,57 @@
|
|
|
14
14
|
"test:e2e": "npx playwright test",
|
|
15
15
|
"command": "tsx ./src/cli.ts"
|
|
16
16
|
},
|
|
17
|
+
"workspaces": [
|
|
18
|
+
"provider"
|
|
19
|
+
],
|
|
17
20
|
"author": "",
|
|
18
21
|
"license": "MIT",
|
|
19
22
|
"dependencies": {
|
|
20
|
-
"@maplibre/maplibre-gl-style-spec": "^
|
|
21
|
-
"@types/geojson": "^7946.0.14",
|
|
22
|
-
"@types/jsonminify": "^0.4.3",
|
|
23
|
+
"@maplibre/maplibre-gl-style-spec": "^23.1.0",
|
|
23
24
|
"@unvt/sprite-one": "^0.1.1",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
25
|
+
"@watergis/maplibre-gl-legend": "^2.0.5",
|
|
26
|
+
"axios": "^1.8.4",
|
|
27
|
+
"commander": "^13.1.0",
|
|
28
|
+
"glob": "^11.0.1",
|
|
27
29
|
"js-yaml": "^4.1.0",
|
|
28
30
|
"jsonminify": "^0.4.2",
|
|
31
|
+
"maplibre-gl": "^5.3.0",
|
|
29
32
|
"node-watch": "^0.7.4",
|
|
30
33
|
"open": "^10.1.0",
|
|
34
|
+
"pmtiles": "^4.3.0",
|
|
35
|
+
"vite": "^6.2.5",
|
|
31
36
|
"ws": "^8.18.1"
|
|
32
37
|
},
|
|
33
38
|
"devDependencies": {
|
|
34
|
-
"@eslint/eslintrc": "^3.1
|
|
35
|
-
"@eslint/js": "^9.
|
|
36
|
-
"@playwright/test": "^1.
|
|
37
|
-
"@types/chai": "^
|
|
38
|
-
"@types/chai-as-promised": "^
|
|
39
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
40
|
+
"@eslint/js": "^9.24.0",
|
|
41
|
+
"@playwright/test": "^1.15.1",
|
|
42
|
+
"@types/chai": "^5.2.1",
|
|
43
|
+
"@types/chai-as-promised": "^8.0.2",
|
|
39
44
|
"@types/fs-extra": "^11.0.4",
|
|
45
|
+
"@types/geojson": "^7946.0.16",
|
|
40
46
|
"@types/js-yaml": "^4.0.9",
|
|
47
|
+
"@types/jsonminify": "^0.4.3",
|
|
41
48
|
"@types/mapbox__point-geometry": "^0.1.4",
|
|
42
|
-
"@types/mocha": "^10.0.
|
|
43
|
-
"@types/node": "^22.
|
|
44
|
-
"@types/ws": "^8.18.
|
|
45
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
46
|
-
"@typescript-eslint/parser": "^8.
|
|
47
|
-
"chai": "^5.
|
|
48
|
-
"chai-as-promised": "^8.0.
|
|
49
|
-
"eslint": "^9.
|
|
50
|
-
"eslint-config-prettier": "^
|
|
51
|
-
"eslint-plugin-prettier": "^5.2.
|
|
52
|
-
"fs-extra": "^11.
|
|
49
|
+
"@types/mocha": "^10.0.10",
|
|
50
|
+
"@types/node": "^22.14.0",
|
|
51
|
+
"@types/ws": "^8.18.1",
|
|
52
|
+
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
|
53
|
+
"@typescript-eslint/parser": "^8.29.1",
|
|
54
|
+
"chai": "^5.2.0",
|
|
55
|
+
"chai-as-promised": "^8.0.1",
|
|
56
|
+
"eslint": "^9.24.0",
|
|
57
|
+
"eslint-config-prettier": "^10.1.1",
|
|
58
|
+
"eslint-plugin-prettier": "^5.2.6",
|
|
59
|
+
"fs-extra": "^11.3.0",
|
|
53
60
|
"kill-port-process": "^3.2.1",
|
|
54
|
-
"mocha": "^
|
|
61
|
+
"mocha": "^11.1.0",
|
|
55
62
|
"node-abort-controller": "^3.1.1",
|
|
56
|
-
"prettier": "^3.
|
|
63
|
+
"prettier": "^3.5.3",
|
|
57
64
|
"ts-node": "^10.9.2",
|
|
58
65
|
"tsconfig-paths": "^4.2.0",
|
|
59
66
|
"tsx": "^4.19.3",
|
|
60
|
-
"typescript": "^5.
|
|
67
|
+
"typescript": "^5.8.3"
|
|
61
68
|
},
|
|
62
69
|
"type": "module"
|
|
63
70
|
}
|
package/provider/index.html
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html id="charites-default">
|
|
3
3
|
<head>
|
|
4
|
-
<link rel="stylesheet" href="/app.css" />
|
|
5
|
-
<link href='https://cdn.jsdelivr.net/npm/maplibre-gl/dist/maplibre-gl.css' rel='stylesheet' />
|
|
6
|
-
<link href='https://cdn.jsdelivr.net/npm/@watergis/maplibre-gl-legend@2.0.5/dist/maplibre-gl-legend.css' rel='stylesheet' />
|
|
7
4
|
<title>Charites Live Preview</title>
|
|
8
|
-
<script src='https://cdn.jsdelivr.net/npm/maplibre-gl/dist/maplibre-gl.js'></script>
|
|
9
|
-
<script src="https://cdn.jsdelivr.net/npm/@watergis/maplibre-gl-legend@2.0.5/dist/maplibre-gl-legend.umd.js"></script>
|
|
10
|
-
<script src="https://cdn.jsdelivr.net/npm/pmtiles@4.3.0/dist/pmtiles.js"></script>
|
|
11
5
|
</head>
|
|
12
6
|
<body>
|
|
13
7
|
<div class="overlay">
|
|
@@ -18,11 +12,6 @@
|
|
|
18
12
|
<label><input type="checkbox" id="showPadding">show Padding</label>
|
|
19
13
|
</div>
|
|
20
14
|
<div id="map"></div>
|
|
21
|
-
<script type="
|
|
22
|
-
let protocol = new pmtiles.Protocol()
|
|
23
|
-
maplibregl.addProtocol('pmtiles', protocol.tile)
|
|
24
|
-
</script>
|
|
25
|
-
<script type="text/javascript" src="/shared.js"></script>
|
|
26
|
-
<script type="text/javascript" src="/app.js"></script>
|
|
15
|
+
<script type="module" src="./src/main.ts"></script>
|
|
27
16
|
</body>
|
|
28
17
|
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Map,
|
|
3
|
+
MapOptions,
|
|
4
|
+
NavigationControl,
|
|
5
|
+
addProtocol,
|
|
6
|
+
IControl,
|
|
7
|
+
} from 'maplibre-gl'
|
|
8
|
+
import 'maplibre-gl/dist/maplibre-gl.css'
|
|
9
|
+
import { MaplibreLegendControl } from '@watergis/maplibre-gl-legend'
|
|
10
|
+
import '@watergis/maplibre-gl-legend/dist/maplibre-gl-legend.css'
|
|
11
|
+
import { Protocol } from 'pmtiles'
|
|
12
|
+
import './app.css'
|
|
13
|
+
|
|
14
|
+
const protocol = new Protocol()
|
|
15
|
+
addProtocol('pmtiles', protocol.tile)
|
|
16
|
+
|
|
17
|
+
class Charites {
|
|
18
|
+
constructor() {}
|
|
19
|
+
|
|
20
|
+
initializeWebSocket = (map: Map) => {
|
|
21
|
+
const isHttps = window.location.protocol === 'https:'
|
|
22
|
+
const wsProtocol = isHttps ? 'wss' : 'ws'
|
|
23
|
+
const socket = new WebSocket(`${wsProtocol}://${window.location.host}`)
|
|
24
|
+
|
|
25
|
+
socket.addEventListener('message', (message) => {
|
|
26
|
+
const data = JSON.parse(message.data)
|
|
27
|
+
if (data.event === 'styleUpdate') {
|
|
28
|
+
console.log('styleUpdate', data)
|
|
29
|
+
map.setStyle(`//${window.location.host}/style.json`)
|
|
30
|
+
} else if (data.event === 'spriteUpdate') {
|
|
31
|
+
map.setStyle(`//${window.location.host}/style.json`, {
|
|
32
|
+
diff: false,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
parseMapStyle = async () => {
|
|
39
|
+
const mapStyleUrl = `//${window.location.host}/style.json`
|
|
40
|
+
const mapStyleRes = await fetch(mapStyleUrl)
|
|
41
|
+
const mapStyleJson = await mapStyleRes.json()
|
|
42
|
+
|
|
43
|
+
// detect center & zoom from map style json
|
|
44
|
+
let center = mapStyleJson.hasOwnProperty('center')
|
|
45
|
+
? mapStyleJson.center
|
|
46
|
+
: undefined
|
|
47
|
+
let zoom = mapStyleJson.hasOwnProperty('zoom')
|
|
48
|
+
? mapStyleJson.zoom
|
|
49
|
+
: undefined
|
|
50
|
+
|
|
51
|
+
// detect center & zoom from tile json
|
|
52
|
+
if (center === undefined || zoom === undefined) {
|
|
53
|
+
for (const sourceName in mapStyleJson.sources) {
|
|
54
|
+
if (
|
|
55
|
+
mapStyleJson.sources[sourceName].type === 'vector' &&
|
|
56
|
+
mapStyleJson.sources[sourceName].hasOwnProperty('url')
|
|
57
|
+
) {
|
|
58
|
+
const mapTileUrl = mapStyleJson.sources[sourceName].url
|
|
59
|
+
const mapTileRes = await fetch(mapTileUrl)
|
|
60
|
+
const mapTileJson = await mapTileRes.json()
|
|
61
|
+
if (center === undefined) {
|
|
62
|
+
const bounds = mapTileJson.bounds
|
|
63
|
+
center = mapTileJson.center
|
|
64
|
+
if (!center && bounds) {
|
|
65
|
+
center = [
|
|
66
|
+
(bounds[0] + bounds[2]) / 2,
|
|
67
|
+
(bounds[1] + bounds[3]) / 2,
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (zoom === undefined) {
|
|
72
|
+
zoom = (mapTileJson.minzoom + mapTileJson.maxzoom) / 2
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
style: mapStyleUrl,
|
|
80
|
+
center,
|
|
81
|
+
zoom,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setupDebugCheckbox = (map: Map) => {
|
|
86
|
+
const properties = [
|
|
87
|
+
'showTileBoundaries',
|
|
88
|
+
'showCollisionBoxes',
|
|
89
|
+
'showPadding',
|
|
90
|
+
]
|
|
91
|
+
for (const property of properties) {
|
|
92
|
+
const control = document.getElementById(property) as HTMLInputElement
|
|
93
|
+
const clickHandler = () => {
|
|
94
|
+
const checked = control.checked
|
|
95
|
+
map[property] = checked
|
|
96
|
+
}
|
|
97
|
+
clickHandler()
|
|
98
|
+
control.addEventListener('click', clickHandler)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const init = async () => {
|
|
104
|
+
const charites = new Charites()
|
|
105
|
+
const { style, center, zoom } = await charites.parseMapStyle()
|
|
106
|
+
const options: MapOptions = {
|
|
107
|
+
container: 'map',
|
|
108
|
+
hash: true,
|
|
109
|
+
maxPitch: 85,
|
|
110
|
+
style,
|
|
111
|
+
}
|
|
112
|
+
if (center) {
|
|
113
|
+
options.center = center
|
|
114
|
+
}
|
|
115
|
+
if (zoom) {
|
|
116
|
+
options.zoom = zoom
|
|
117
|
+
}
|
|
118
|
+
const map = new Map(options)
|
|
119
|
+
|
|
120
|
+
charites.initializeWebSocket(map)
|
|
121
|
+
|
|
122
|
+
map.addControl(new NavigationControl(), 'top-right')
|
|
123
|
+
map.addControl(
|
|
124
|
+
new MaplibreLegendControl(
|
|
125
|
+
{},
|
|
126
|
+
{
|
|
127
|
+
showDefault: true,
|
|
128
|
+
showCheckbox: true,
|
|
129
|
+
onlyRendered: true,
|
|
130
|
+
reverseOrder: true,
|
|
131
|
+
},
|
|
132
|
+
) as unknown as IControl,
|
|
133
|
+
'bottom-left',
|
|
134
|
+
)
|
|
135
|
+
charites.setupDebugCheckbox(map)
|
|
136
|
+
}
|
|
137
|
+
init()
|
package/src/cli/serve.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
|
+
import { createServer, InlineConfig } from 'vite'
|
|
3
|
+
import { fileURLToPath } from 'url'
|
|
4
|
+
import path from 'path'
|
|
2
5
|
import { serve, serveOptions } from '../commands/serve.js'
|
|
3
6
|
import { error } from '../lib/error.js'
|
|
4
7
|
|
|
@@ -13,6 +16,7 @@ program
|
|
|
13
16
|
)
|
|
14
17
|
.option('--sdf', 'Allows to use SDF sprite in charites')
|
|
15
18
|
.option('--port [port]', 'Specify custom port')
|
|
19
|
+
.option('--vite-port [vitePort]', 'Specify custom port for vite server')
|
|
16
20
|
.option('--no-open', "Don't open the preview in the default browser")
|
|
17
21
|
.action(async (source: string, serveOptions: serveOptions) => {
|
|
18
22
|
const options: serveOptions = program.opts()
|
|
@@ -20,11 +24,26 @@ program
|
|
|
20
24
|
options.spriteInput = serveOptions.spriteInput
|
|
21
25
|
options.open = serveOptions.open
|
|
22
26
|
options.sdf = serveOptions.sdf
|
|
27
|
+
options.vitePort = serveOptions.vitePort
|
|
23
28
|
try {
|
|
29
|
+
await startProviderDevServer(options)
|
|
24
30
|
await serve(source, program.opts())
|
|
25
31
|
} catch (e) {
|
|
26
32
|
error(e)
|
|
27
33
|
}
|
|
28
34
|
})
|
|
29
35
|
|
|
36
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
37
|
+
const __dirname = path.dirname(__filename)
|
|
38
|
+
async function startProviderDevServer(options: serveOptions) {
|
|
39
|
+
const config: InlineConfig = {
|
|
40
|
+
root: path.resolve(__dirname, '../../provider'),
|
|
41
|
+
server: {
|
|
42
|
+
port: parseInt(options.vitePort ?? '5137'),
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const viteServer = await createServer(config)
|
|
47
|
+
await viteServer.listen()
|
|
48
|
+
}
|
|
30
49
|
export default program
|
package/src/commands/serve.ts
CHANGED
|
@@ -10,11 +10,11 @@ import watch from 'node-watch'
|
|
|
10
10
|
|
|
11
11
|
import { parser } from '../lib/yaml-parser.js'
|
|
12
12
|
import { validateStyle } from '../lib/validate-style.js'
|
|
13
|
-
import { providerDir } from '../lib/defaultValues.js'
|
|
14
13
|
import { buildSprite } from '../lib/build-sprite.js'
|
|
15
14
|
|
|
16
15
|
export interface serveOptions {
|
|
17
16
|
port?: string
|
|
17
|
+
vitePort?: string
|
|
18
18
|
spriteInput?: string
|
|
19
19
|
open?: boolean
|
|
20
20
|
sdf?: boolean
|
|
@@ -73,15 +73,6 @@ export async function serve(source: string, options: serveOptions) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
switch (url) {
|
|
76
|
-
case '/':
|
|
77
|
-
res.statusCode = 200
|
|
78
|
-
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
|
79
|
-
const content = fs.readFileSync(
|
|
80
|
-
path.join(providerDir, 'index.html'),
|
|
81
|
-
'utf-8',
|
|
82
|
-
)
|
|
83
|
-
res.end(content)
|
|
84
|
-
break
|
|
85
76
|
case '/style.json':
|
|
86
77
|
let style
|
|
87
78
|
try {
|
|
@@ -100,24 +91,40 @@ export async function serve(source: string, options: serveOptions) {
|
|
|
100
91
|
res.setHeader('Cache-Control', 'no-store')
|
|
101
92
|
res.end(JSON.stringify(style))
|
|
102
93
|
break
|
|
103
|
-
case '/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
res.end(css)
|
|
108
|
-
break
|
|
109
|
-
case `/app.js`:
|
|
110
|
-
res.statusCode = 200
|
|
111
|
-
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8')
|
|
112
|
-
const app = fs.readFileSync(path.join(providerDir, 'app.js'), 'utf-8')
|
|
113
|
-
const js = app.replace('___PORT___', `${port}`)
|
|
114
|
-
res.end(js)
|
|
115
|
-
break
|
|
116
|
-
default:
|
|
94
|
+
case '/sprite.json':
|
|
95
|
+
case '/sprite@2x.json':
|
|
96
|
+
case '/sprite.png':
|
|
97
|
+
case '/sprite@2x.png':
|
|
117
98
|
res.statusCode = 404
|
|
118
99
|
res.setHeader('Content-Type', 'text/plain; charset=UTF-8')
|
|
119
100
|
res.end('Not found')
|
|
120
101
|
break
|
|
102
|
+
default:
|
|
103
|
+
const vitePort = parseInt(options.vitePort || '5137')
|
|
104
|
+
const proxyReq = http.request(
|
|
105
|
+
{
|
|
106
|
+
hostname: 'localhost',
|
|
107
|
+
port: vitePort,
|
|
108
|
+
path: url,
|
|
109
|
+
method: req.method,
|
|
110
|
+
headers: req.headers,
|
|
111
|
+
},
|
|
112
|
+
(viteRes) => {
|
|
113
|
+
res.writeHead(viteRes.statusCode || 500, viteRes.headers)
|
|
114
|
+
viteRes.pipe(res, {
|
|
115
|
+
end: true,
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
req.pipe(proxyReq, {
|
|
120
|
+
end: true,
|
|
121
|
+
})
|
|
122
|
+
proxyReq.on('error', (err) => {
|
|
123
|
+
console.error('Error with proxy request:', err)
|
|
124
|
+
res.statusCode = 500
|
|
125
|
+
res.end('Bad gateway: failed to proxy to Vite dev server')
|
|
126
|
+
})
|
|
127
|
+
break
|
|
121
128
|
}
|
|
122
129
|
})
|
|
123
130
|
|
|
@@ -131,11 +138,11 @@ export async function serve(source: string, options: serveOptions) {
|
|
|
131
138
|
|
|
132
139
|
const wss = new WebSocketServer({ server })
|
|
133
140
|
|
|
134
|
-
wss.on('connection', (ws) => {
|
|
141
|
+
wss.on('connection', (ws: WebSocket) => {
|
|
135
142
|
const watcher = watch(
|
|
136
143
|
path.dirname(sourcePath),
|
|
137
144
|
{ recursive: true, filter: /\.yml$|\.svg$/i },
|
|
138
|
-
(event, file) => {
|
|
145
|
+
(event: string, file: string) => {
|
|
139
146
|
console.log(`${(event || '').toUpperCase()}: ${file}`)
|
|
140
147
|
try {
|
|
141
148
|
if (file?.toLowerCase().endsWith('.yml')) {
|
|
@@ -161,7 +168,7 @@ export async function serve(source: string, options: serveOptions) {
|
|
|
161
168
|
}
|
|
162
169
|
},
|
|
163
170
|
)
|
|
164
|
-
|
|
171
|
+
wss.on('close', () => {
|
|
165
172
|
watcher.close()
|
|
166
173
|
})
|
|
167
174
|
})
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
validateStyleMin,
|
|
2
3
|
StyleSpecification,
|
|
3
4
|
} from '@maplibre/maplibre-gl-style-spec'
|
|
4
5
|
|
|
5
6
|
export function validateStyle(style: StyleSpecification): void {
|
|
6
|
-
const result =
|
|
7
|
+
const result = validateStyleMin(style)
|
|
7
8
|
|
|
8
9
|
const errors = []
|
|
9
10
|
for (let i = 0; i < result.length; i++) {
|
package/src/lib/yaml-writer.ts
CHANGED
|
@@ -43,10 +43,13 @@ class IncFileTag {
|
|
|
43
43
|
|
|
44
44
|
const INC_PATH_TYPE = new YAML.Type('tag:yaml.org,2002:inc/file', {
|
|
45
45
|
kind: 'scalar',
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
resolve: (data: any) => data,
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
construct: (data: any) => new IncFileTag(data),
|
|
48
50
|
instanceOf: IncFileTag,
|
|
49
|
-
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
represent: (tag: any) => (tag as IncFileTag).value,
|
|
50
53
|
})
|
|
51
54
|
const INC_PATH_OUTPUT_SCHEMA = YAML.DEFAULT_SCHEMA.extend([INC_PATH_TYPE])
|
|
52
55
|
|
package/src/types/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './tilejson'
|
|
2
|
-
export * from './metadatajson'
|
|
3
|
-
export * from './vector_layers'
|
|
1
|
+
export * from './tilejson.js'
|
|
2
|
+
export * from './metadatajson.js'
|
|
3
|
+
export * from './vector_layers.js'
|
package/src/types/tilejson.ts
CHANGED
package/tsconfig.json
CHANGED
|
@@ -13,13 +13,21 @@
|
|
|
13
13
|
"experimentalDecorators": true,
|
|
14
14
|
"emitDecoratorMetadata": true,
|
|
15
15
|
"resolveJsonModule": true,
|
|
16
|
-
"
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"module": "Preserve",
|
|
17
19
|
"moduleResolution": "node",
|
|
18
20
|
"outDir": "./dist",
|
|
19
21
|
"baseUrl": ".",
|
|
20
22
|
"paths": {
|
|
21
23
|
"*": ["node_modules/*", "src/types/*"]
|
|
22
|
-
}
|
|
24
|
+
},
|
|
25
|
+
"types": [
|
|
26
|
+
"mocha",
|
|
27
|
+
"chai",
|
|
28
|
+
"node"
|
|
29
|
+
],
|
|
30
|
+
"typeRoots": ["./node_modules/@types", "./src/types"]
|
|
23
31
|
},
|
|
24
32
|
"include": ["src/**/*"],
|
|
25
33
|
"exclude": ["node_modules", "dist"]
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from 'url';
|
|
2
|
-
import { dirname } from 'path';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = dirname(__filename);
|
|
6
|
-
export const providerDir = path.join(path.dirname(path.dirname(__dirname)), 'provider');
|
package/provider/app.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
;(async () => {
|
|
2
|
-
window._charites = {
|
|
3
|
-
initializeWebSocket: function (map) {
|
|
4
|
-
const isHttps = window.location.protocol === 'https:'
|
|
5
|
-
const wsProtocol = isHttps ? 'wss' : 'ws'
|
|
6
|
-
const socket = new WebSocket(`${wsProtocol}://${window.location.host}`)
|
|
7
|
-
|
|
8
|
-
socket.addEventListener('message', (message) => {
|
|
9
|
-
const data = JSON.parse(message.data)
|
|
10
|
-
if (data.event === 'styleUpdate') {
|
|
11
|
-
map.setStyle(`//${window.location.host}/style.json`)
|
|
12
|
-
} else if (data.event === 'spriteUpdate') {
|
|
13
|
-
map.setStyle(`//${window.location.host}/style.json`, {
|
|
14
|
-
diff: false,
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
|
-
})
|
|
18
|
-
},
|
|
19
|
-
parseMapStyle: async function () {
|
|
20
|
-
const mapStyleUrl = `//${window.location.host}/style.json`
|
|
21
|
-
const mapStyleRes = await fetch(mapStyleUrl)
|
|
22
|
-
const mapStyleJson = await mapStyleRes.json()
|
|
23
|
-
|
|
24
|
-
// detect center & zoom from map style json
|
|
25
|
-
let center = mapStyleJson.hasOwnProperty('center')
|
|
26
|
-
? mapStyleJson.center
|
|
27
|
-
: undefined
|
|
28
|
-
let zoom = mapStyleJson.hasOwnProperty('zoom')
|
|
29
|
-
? mapStyleJson.zoom
|
|
30
|
-
: undefined
|
|
31
|
-
|
|
32
|
-
// detect center & zoom from tile json
|
|
33
|
-
if (center === undefined || zoom === undefined) {
|
|
34
|
-
for (const sourceName in mapStyleJson.sources) {
|
|
35
|
-
if (
|
|
36
|
-
mapStyleJson.sources[sourceName].type === 'vector' &&
|
|
37
|
-
mapStyleJson.sources[sourceName].hasOwnProperty('url')
|
|
38
|
-
) {
|
|
39
|
-
const mapTileUrl = mapStyleJson.sources[sourceName].url
|
|
40
|
-
const mapTileRes = await fetch(mapTileUrl)
|
|
41
|
-
const mapTileJson = await mapTileRes.json()
|
|
42
|
-
if (center === undefined) {
|
|
43
|
-
const bounds = mapTileJson.bounds
|
|
44
|
-
center = mapTileJson.center
|
|
45
|
-
if (!center && bounds) {
|
|
46
|
-
center = [
|
|
47
|
-
(bounds[0] + bounds[2]) / 2,
|
|
48
|
-
(bounds[1] + bounds[3]) / 2,
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (zoom === undefined) {
|
|
53
|
-
zoom = (mapTileJson.minzoom + mapTileJson.maxzoom) / 2
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
style: mapStyleUrl,
|
|
61
|
-
center,
|
|
62
|
-
zoom,
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
setupDebugCheckboxes: function (map) {
|
|
66
|
-
const properties = [
|
|
67
|
-
'showTileBoundaries',
|
|
68
|
-
'showCollisionBoxes',
|
|
69
|
-
'showPadding',
|
|
70
|
-
]
|
|
71
|
-
for (const property of properties) {
|
|
72
|
-
const control = document.getElementById(property)
|
|
73
|
-
const clickHandler = function () {
|
|
74
|
-
const checked = control.checked
|
|
75
|
-
map[property] = checked
|
|
76
|
-
}
|
|
77
|
-
clickHandler()
|
|
78
|
-
control.addEventListener('click', clickHandler)
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const { style, center, zoom } = await window._charites.parseMapStyle()
|
|
84
|
-
const options = {
|
|
85
|
-
container: 'map',
|
|
86
|
-
hash: true,
|
|
87
|
-
maxPitch: 85,
|
|
88
|
-
style,
|
|
89
|
-
}
|
|
90
|
-
if (center) options.center = center
|
|
91
|
-
if (zoom) options.zoom = zoom
|
|
92
|
-
const map = new maplibregl.Map(options)
|
|
93
|
-
|
|
94
|
-
window._charites.initializeWebSocket(map)
|
|
95
|
-
|
|
96
|
-
map.addControl(new maplibregl.NavigationControl(), 'top-right')
|
|
97
|
-
|
|
98
|
-
map.addControl(
|
|
99
|
-
new MaplibreLegendControl.MaplibreLegendControl(
|
|
100
|
-
{},
|
|
101
|
-
{
|
|
102
|
-
showDefault: true,
|
|
103
|
-
showCheckbox: true,
|
|
104
|
-
onlyRendered: true,
|
|
105
|
-
reverseOrder: true,
|
|
106
|
-
},
|
|
107
|
-
),
|
|
108
|
-
'bottom-left',
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
window._charites.setupDebugCheckboxes(map)
|
|
112
|
-
})()
|
package/src/lib/defaultValues.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from 'url'
|
|
2
|
-
import { dirname } from 'path'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
6
|
-
const __dirname = dirname(__filename)
|
|
7
|
-
export const providerDir = path.join(
|
|
8
|
-
path.dirname(path.dirname(__dirname)),
|
|
9
|
-
'provider',
|
|
10
|
-
)
|