├── .gitignore
├── README.md
├── image.png
├── index.html
├── package-lock.json
├── package.json
├── public
├── favicon.svg
├── magma-bg-pattern.jpg
└── water-bg-pattern-03.jpg
├── src
├── main.ts
├── protocol
│ ├── debug.ts
│ ├── image.ts
│ ├── raster
│ │ ├── index.ts
│ │ ├── shader
│ │ │ ├── fragment.glsl
│ │ │ └── vertex.glsl
│ │ └── worker.ts
│ └── terrain
│ │ ├── index.ts
│ │ ├── shader
│ │ ├── fragment.glsl
│ │ └── vertex.glsl
│ │ └── worker.ts
├── style.css
├── utils.ts
└── vite-env.d.ts
├── tsconfig.json
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terrain Visualizer🏔️
2 |
3 | An interactive application that visualizes terrain using elevation tiles.
4 |
5 | 
6 |
7 | ## DEMO
8 |
9 | https://forestacdev.github.io/maplibre-terrain-visualizer/
10 |
11 | ## 概要
12 |
13 | 標高タイルからインタラクティブに地形を可視化するアプリケーションです。
14 |
15 | ※ このアプリケーションで可視化されたデータはあくまで視覚的なものであり、その正確性を保証するものではありません。
16 |
17 | ## Qiita 記事
18 |
19 | https://qiita.com/satoshi7190/items/9ae4d99601eb67880188
20 |
21 | ## データソース
22 |
23 | - [国土地理院 地理院標高タイル](https://maps.gsi.go.jp/development/ichiran.html)
24 | - [産総研 シームレス標高タイル](https://tiles.gsj.jp/tiles/elev/tiles.html#h_hyogo)
25 | - [G 空間情報センター 栃木県「数値標高モデル(DEM)0.5m」](https://www.geospatial.jp/ckan/dataset/dem05_tochigi)
26 | - [G 空間情報センター 高知県「数値標高モデル(DEM)0.5m」](https://www.geospatial.jp/ckan/dataset/dem05_kochi)
27 | - [G 空間情報センター 富山県・DEM/DCHM/林相識別図マップタイル(林野庁加工)](https://www.geospatial.jp/ckan/dataset/rinya-toyama-maptiles)
28 | - [G 空間情報センター 林野庁・数値標高モデル DEM0.5m(長岡地域 2024)」](https://www.geospatial.jp/ckan/dataset/rinya-dem-nagaoka2024)
29 | - [G 空間情報センター 林野庁・数値樹冠高モデル DCHM0.5 m(長岡地域 2024)」](https://www.geospatial.jp/ckan/dataset/rinya-dchm-nagaoka2024)
30 | - [AWS Terrain Tiles](https://aws.amazon.com/marketplace/pp/prodview-x7vtai3hasf26#resources/)
31 |
32 | ## 開発環境
33 |
34 | ```
35 | npm install
36 | npm run dev
37 | ```
38 |
--------------------------------------------------------------------------------
/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forestacdev/maplibre-terrain-visualizer/dd058a6135738d9c2d1238761cb5924de1aa0eb3/image.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Terrain Visualizer
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "maplibre-terrain-tiles-viewer",
3 | "version": "0.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "maplibre-terrain-tiles-viewer",
9 | "version": "0.0.0",
10 | "dependencies": {
11 | "chroma-js": "^3.1.2",
12 | "colormap": "^2.3.2",
13 | "glsl-colormap": "^1.0.1",
14 | "lil-gui": "^0.19.2",
15 | "lodash.debounce": "^4.0.8",
16 | "maplibre-gl": "^4.7.1"
17 | },
18 | "devDependencies": {
19 | "@types/chroma-js": "^2.4.4",
20 | "@types/colormap": "^2.3.4",
21 | "@types/lodash.debounce": "^4.0.9",
22 | "gh-pages": "^6.3.0",
23 | "glslify": "^7.1.1",
24 | "typescript": "^5.5.3",
25 | "vite": "^5.4.8"
26 | }
27 | },
28 | "node_modules/@choojs/findup": {
29 | "version": "0.2.1",
30 | "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz",
31 | "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==",
32 | "dev": true,
33 | "dependencies": {
34 | "commander": "^2.15.1"
35 | },
36 | "bin": {
37 | "findup": "bin/findup.js"
38 | }
39 | },
40 | "node_modules/@esbuild/aix-ppc64": {
41 | "version": "0.21.5",
42 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
43 | "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
44 | "cpu": [
45 | "ppc64"
46 | ],
47 | "dev": true,
48 | "optional": true,
49 | "os": [
50 | "aix"
51 | ],
52 | "engines": {
53 | "node": ">=12"
54 | }
55 | },
56 | "node_modules/@esbuild/android-arm": {
57 | "version": "0.21.5",
58 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
59 | "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
60 | "cpu": [
61 | "arm"
62 | ],
63 | "dev": true,
64 | "optional": true,
65 | "os": [
66 | "android"
67 | ],
68 | "engines": {
69 | "node": ">=12"
70 | }
71 | },
72 | "node_modules/@esbuild/android-arm64": {
73 | "version": "0.21.5",
74 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
75 | "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
76 | "cpu": [
77 | "arm64"
78 | ],
79 | "dev": true,
80 | "optional": true,
81 | "os": [
82 | "android"
83 | ],
84 | "engines": {
85 | "node": ">=12"
86 | }
87 | },
88 | "node_modules/@esbuild/android-x64": {
89 | "version": "0.21.5",
90 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
91 | "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
92 | "cpu": [
93 | "x64"
94 | ],
95 | "dev": true,
96 | "optional": true,
97 | "os": [
98 | "android"
99 | ],
100 | "engines": {
101 | "node": ">=12"
102 | }
103 | },
104 | "node_modules/@esbuild/darwin-arm64": {
105 | "version": "0.21.5",
106 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
107 | "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
108 | "cpu": [
109 | "arm64"
110 | ],
111 | "dev": true,
112 | "optional": true,
113 | "os": [
114 | "darwin"
115 | ],
116 | "engines": {
117 | "node": ">=12"
118 | }
119 | },
120 | "node_modules/@esbuild/darwin-x64": {
121 | "version": "0.21.5",
122 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
123 | "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
124 | "cpu": [
125 | "x64"
126 | ],
127 | "dev": true,
128 | "optional": true,
129 | "os": [
130 | "darwin"
131 | ],
132 | "engines": {
133 | "node": ">=12"
134 | }
135 | },
136 | "node_modules/@esbuild/freebsd-arm64": {
137 | "version": "0.21.5",
138 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
139 | "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
140 | "cpu": [
141 | "arm64"
142 | ],
143 | "dev": true,
144 | "optional": true,
145 | "os": [
146 | "freebsd"
147 | ],
148 | "engines": {
149 | "node": ">=12"
150 | }
151 | },
152 | "node_modules/@esbuild/freebsd-x64": {
153 | "version": "0.21.5",
154 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
155 | "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
156 | "cpu": [
157 | "x64"
158 | ],
159 | "dev": true,
160 | "optional": true,
161 | "os": [
162 | "freebsd"
163 | ],
164 | "engines": {
165 | "node": ">=12"
166 | }
167 | },
168 | "node_modules/@esbuild/linux-arm": {
169 | "version": "0.21.5",
170 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
171 | "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
172 | "cpu": [
173 | "arm"
174 | ],
175 | "dev": true,
176 | "optional": true,
177 | "os": [
178 | "linux"
179 | ],
180 | "engines": {
181 | "node": ">=12"
182 | }
183 | },
184 | "node_modules/@esbuild/linux-arm64": {
185 | "version": "0.21.5",
186 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
187 | "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
188 | "cpu": [
189 | "arm64"
190 | ],
191 | "dev": true,
192 | "optional": true,
193 | "os": [
194 | "linux"
195 | ],
196 | "engines": {
197 | "node": ">=12"
198 | }
199 | },
200 | "node_modules/@esbuild/linux-ia32": {
201 | "version": "0.21.5",
202 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
203 | "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
204 | "cpu": [
205 | "ia32"
206 | ],
207 | "dev": true,
208 | "optional": true,
209 | "os": [
210 | "linux"
211 | ],
212 | "engines": {
213 | "node": ">=12"
214 | }
215 | },
216 | "node_modules/@esbuild/linux-loong64": {
217 | "version": "0.21.5",
218 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
219 | "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
220 | "cpu": [
221 | "loong64"
222 | ],
223 | "dev": true,
224 | "optional": true,
225 | "os": [
226 | "linux"
227 | ],
228 | "engines": {
229 | "node": ">=12"
230 | }
231 | },
232 | "node_modules/@esbuild/linux-mips64el": {
233 | "version": "0.21.5",
234 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
235 | "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
236 | "cpu": [
237 | "mips64el"
238 | ],
239 | "dev": true,
240 | "optional": true,
241 | "os": [
242 | "linux"
243 | ],
244 | "engines": {
245 | "node": ">=12"
246 | }
247 | },
248 | "node_modules/@esbuild/linux-ppc64": {
249 | "version": "0.21.5",
250 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
251 | "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
252 | "cpu": [
253 | "ppc64"
254 | ],
255 | "dev": true,
256 | "optional": true,
257 | "os": [
258 | "linux"
259 | ],
260 | "engines": {
261 | "node": ">=12"
262 | }
263 | },
264 | "node_modules/@esbuild/linux-riscv64": {
265 | "version": "0.21.5",
266 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
267 | "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
268 | "cpu": [
269 | "riscv64"
270 | ],
271 | "dev": true,
272 | "optional": true,
273 | "os": [
274 | "linux"
275 | ],
276 | "engines": {
277 | "node": ">=12"
278 | }
279 | },
280 | "node_modules/@esbuild/linux-s390x": {
281 | "version": "0.21.5",
282 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
283 | "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
284 | "cpu": [
285 | "s390x"
286 | ],
287 | "dev": true,
288 | "optional": true,
289 | "os": [
290 | "linux"
291 | ],
292 | "engines": {
293 | "node": ">=12"
294 | }
295 | },
296 | "node_modules/@esbuild/linux-x64": {
297 | "version": "0.21.5",
298 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
299 | "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
300 | "cpu": [
301 | "x64"
302 | ],
303 | "dev": true,
304 | "optional": true,
305 | "os": [
306 | "linux"
307 | ],
308 | "engines": {
309 | "node": ">=12"
310 | }
311 | },
312 | "node_modules/@esbuild/netbsd-x64": {
313 | "version": "0.21.5",
314 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
315 | "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
316 | "cpu": [
317 | "x64"
318 | ],
319 | "dev": true,
320 | "optional": true,
321 | "os": [
322 | "netbsd"
323 | ],
324 | "engines": {
325 | "node": ">=12"
326 | }
327 | },
328 | "node_modules/@esbuild/openbsd-x64": {
329 | "version": "0.21.5",
330 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
331 | "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
332 | "cpu": [
333 | "x64"
334 | ],
335 | "dev": true,
336 | "optional": true,
337 | "os": [
338 | "openbsd"
339 | ],
340 | "engines": {
341 | "node": ">=12"
342 | }
343 | },
344 | "node_modules/@esbuild/sunos-x64": {
345 | "version": "0.21.5",
346 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
347 | "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
348 | "cpu": [
349 | "x64"
350 | ],
351 | "dev": true,
352 | "optional": true,
353 | "os": [
354 | "sunos"
355 | ],
356 | "engines": {
357 | "node": ">=12"
358 | }
359 | },
360 | "node_modules/@esbuild/win32-arm64": {
361 | "version": "0.21.5",
362 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
363 | "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
364 | "cpu": [
365 | "arm64"
366 | ],
367 | "dev": true,
368 | "optional": true,
369 | "os": [
370 | "win32"
371 | ],
372 | "engines": {
373 | "node": ">=12"
374 | }
375 | },
376 | "node_modules/@esbuild/win32-ia32": {
377 | "version": "0.21.5",
378 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
379 | "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
380 | "cpu": [
381 | "ia32"
382 | ],
383 | "dev": true,
384 | "optional": true,
385 | "os": [
386 | "win32"
387 | ],
388 | "engines": {
389 | "node": ">=12"
390 | }
391 | },
392 | "node_modules/@esbuild/win32-x64": {
393 | "version": "0.21.5",
394 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
395 | "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
396 | "cpu": [
397 | "x64"
398 | ],
399 | "dev": true,
400 | "optional": true,
401 | "os": [
402 | "win32"
403 | ],
404 | "engines": {
405 | "node": ">=12"
406 | }
407 | },
408 | "node_modules/@mapbox/geojson-rewind": {
409 | "version": "0.5.2",
410 | "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
411 | "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
412 | "dependencies": {
413 | "get-stream": "^6.0.1",
414 | "minimist": "^1.2.6"
415 | },
416 | "bin": {
417 | "geojson-rewind": "geojson-rewind"
418 | }
419 | },
420 | "node_modules/@mapbox/jsonlint-lines-primitives": {
421 | "version": "2.0.2",
422 | "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
423 | "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
424 | "engines": {
425 | "node": ">= 0.6"
426 | }
427 | },
428 | "node_modules/@mapbox/point-geometry": {
429 | "version": "0.1.0",
430 | "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
431 | "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
432 | },
433 | "node_modules/@mapbox/tiny-sdf": {
434 | "version": "2.0.6",
435 | "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz",
436 | "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA=="
437 | },
438 | "node_modules/@mapbox/unitbezier": {
439 | "version": "0.0.1",
440 | "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
441 | "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="
442 | },
443 | "node_modules/@mapbox/vector-tile": {
444 | "version": "1.3.1",
445 | "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
446 | "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
447 | "dependencies": {
448 | "@mapbox/point-geometry": "~0.1.0"
449 | }
450 | },
451 | "node_modules/@mapbox/whoots-js": {
452 | "version": "3.1.0",
453 | "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
454 | "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
455 | "engines": {
456 | "node": ">=6.0.0"
457 | }
458 | },
459 | "node_modules/@maplibre/maplibre-gl-style-spec": {
460 | "version": "20.4.0",
461 | "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz",
462 | "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==",
463 | "dependencies": {
464 | "@mapbox/jsonlint-lines-primitives": "~2.0.2",
465 | "@mapbox/unitbezier": "^0.0.1",
466 | "json-stringify-pretty-compact": "^4.0.0",
467 | "minimist": "^1.2.8",
468 | "quickselect": "^2.0.0",
469 | "rw": "^1.3.3",
470 | "tinyqueue": "^3.0.0"
471 | },
472 | "bin": {
473 | "gl-style-format": "dist/gl-style-format.mjs",
474 | "gl-style-migrate": "dist/gl-style-migrate.mjs",
475 | "gl-style-validate": "dist/gl-style-validate.mjs"
476 | }
477 | },
478 | "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": {
479 | "version": "2.0.0",
480 | "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
481 | "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
482 | },
483 | "node_modules/@nodelib/fs.scandir": {
484 | "version": "2.1.5",
485 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
486 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
487 | "dev": true,
488 | "dependencies": {
489 | "@nodelib/fs.stat": "2.0.5",
490 | "run-parallel": "^1.1.9"
491 | },
492 | "engines": {
493 | "node": ">= 8"
494 | }
495 | },
496 | "node_modules/@nodelib/fs.stat": {
497 | "version": "2.0.5",
498 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
499 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
500 | "dev": true,
501 | "engines": {
502 | "node": ">= 8"
503 | }
504 | },
505 | "node_modules/@nodelib/fs.walk": {
506 | "version": "1.2.8",
507 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
508 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
509 | "dev": true,
510 | "dependencies": {
511 | "@nodelib/fs.scandir": "2.1.5",
512 | "fastq": "^1.6.0"
513 | },
514 | "engines": {
515 | "node": ">= 8"
516 | }
517 | },
518 | "node_modules/@rollup/rollup-android-arm-eabi": {
519 | "version": "4.24.0",
520 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
521 | "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
522 | "cpu": [
523 | "arm"
524 | ],
525 | "dev": true,
526 | "optional": true,
527 | "os": [
528 | "android"
529 | ]
530 | },
531 | "node_modules/@rollup/rollup-android-arm64": {
532 | "version": "4.24.0",
533 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
534 | "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
535 | "cpu": [
536 | "arm64"
537 | ],
538 | "dev": true,
539 | "optional": true,
540 | "os": [
541 | "android"
542 | ]
543 | },
544 | "node_modules/@rollup/rollup-darwin-arm64": {
545 | "version": "4.24.0",
546 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
547 | "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
548 | "cpu": [
549 | "arm64"
550 | ],
551 | "dev": true,
552 | "optional": true,
553 | "os": [
554 | "darwin"
555 | ]
556 | },
557 | "node_modules/@rollup/rollup-darwin-x64": {
558 | "version": "4.24.0",
559 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
560 | "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
561 | "cpu": [
562 | "x64"
563 | ],
564 | "dev": true,
565 | "optional": true,
566 | "os": [
567 | "darwin"
568 | ]
569 | },
570 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
571 | "version": "4.24.0",
572 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
573 | "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
574 | "cpu": [
575 | "arm"
576 | ],
577 | "dev": true,
578 | "optional": true,
579 | "os": [
580 | "linux"
581 | ]
582 | },
583 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
584 | "version": "4.24.0",
585 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
586 | "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
587 | "cpu": [
588 | "arm"
589 | ],
590 | "dev": true,
591 | "optional": true,
592 | "os": [
593 | "linux"
594 | ]
595 | },
596 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
597 | "version": "4.24.0",
598 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
599 | "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
600 | "cpu": [
601 | "arm64"
602 | ],
603 | "dev": true,
604 | "optional": true,
605 | "os": [
606 | "linux"
607 | ]
608 | },
609 | "node_modules/@rollup/rollup-linux-arm64-musl": {
610 | "version": "4.24.0",
611 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
612 | "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
613 | "cpu": [
614 | "arm64"
615 | ],
616 | "dev": true,
617 | "optional": true,
618 | "os": [
619 | "linux"
620 | ]
621 | },
622 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
623 | "version": "4.24.0",
624 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
625 | "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
626 | "cpu": [
627 | "ppc64"
628 | ],
629 | "dev": true,
630 | "optional": true,
631 | "os": [
632 | "linux"
633 | ]
634 | },
635 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
636 | "version": "4.24.0",
637 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
638 | "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
639 | "cpu": [
640 | "riscv64"
641 | ],
642 | "dev": true,
643 | "optional": true,
644 | "os": [
645 | "linux"
646 | ]
647 | },
648 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
649 | "version": "4.24.0",
650 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
651 | "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
652 | "cpu": [
653 | "s390x"
654 | ],
655 | "dev": true,
656 | "optional": true,
657 | "os": [
658 | "linux"
659 | ]
660 | },
661 | "node_modules/@rollup/rollup-linux-x64-gnu": {
662 | "version": "4.24.0",
663 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
664 | "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
665 | "cpu": [
666 | "x64"
667 | ],
668 | "dev": true,
669 | "optional": true,
670 | "os": [
671 | "linux"
672 | ]
673 | },
674 | "node_modules/@rollup/rollup-linux-x64-musl": {
675 | "version": "4.24.0",
676 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
677 | "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
678 | "cpu": [
679 | "x64"
680 | ],
681 | "dev": true,
682 | "optional": true,
683 | "os": [
684 | "linux"
685 | ]
686 | },
687 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
688 | "version": "4.24.0",
689 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
690 | "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
691 | "cpu": [
692 | "arm64"
693 | ],
694 | "dev": true,
695 | "optional": true,
696 | "os": [
697 | "win32"
698 | ]
699 | },
700 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
701 | "version": "4.24.0",
702 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
703 | "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
704 | "cpu": [
705 | "ia32"
706 | ],
707 | "dev": true,
708 | "optional": true,
709 | "os": [
710 | "win32"
711 | ]
712 | },
713 | "node_modules/@rollup/rollup-win32-x64-msvc": {
714 | "version": "4.24.0",
715 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
716 | "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
717 | "cpu": [
718 | "x64"
719 | ],
720 | "dev": true,
721 | "optional": true,
722 | "os": [
723 | "win32"
724 | ]
725 | },
726 | "node_modules/@types/chroma-js": {
727 | "version": "2.4.4",
728 | "resolved": "https://registry.npmjs.org/@types/chroma-js/-/chroma-js-2.4.4.tgz",
729 | "integrity": "sha512-/DTccpHTaKomqussrn+ciEvfW4k6NAHzNzs/sts1TCqg333qNxOhy8TNIoQCmbGG3Tl8KdEhkGAssb1n3mTXiQ==",
730 | "dev": true
731 | },
732 | "node_modules/@types/colormap": {
733 | "version": "2.3.4",
734 | "resolved": "https://registry.npmjs.org/@types/colormap/-/colormap-2.3.4.tgz",
735 | "integrity": "sha512-kKKbv5JXmWieUCqjO1amnf/2wS8pHKdjxnxKanWalI6p3ly5Hjt+/+6oB+Sk+dC02qzNuj6x87xltb+PPxeiNQ==",
736 | "dev": true
737 | },
738 | "node_modules/@types/estree": {
739 | "version": "1.0.6",
740 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
741 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
742 | "dev": true
743 | },
744 | "node_modules/@types/geojson": {
745 | "version": "7946.0.14",
746 | "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
747 | "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
748 | },
749 | "node_modules/@types/geojson-vt": {
750 | "version": "3.2.5",
751 | "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz",
752 | "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==",
753 | "dependencies": {
754 | "@types/geojson": "*"
755 | }
756 | },
757 | "node_modules/@types/lodash": {
758 | "version": "4.17.12",
759 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.12.tgz",
760 | "integrity": "sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==",
761 | "dev": true
762 | },
763 | "node_modules/@types/lodash.debounce": {
764 | "version": "4.0.9",
765 | "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz",
766 | "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==",
767 | "dev": true,
768 | "dependencies": {
769 | "@types/lodash": "*"
770 | }
771 | },
772 | "node_modules/@types/mapbox__point-geometry": {
773 | "version": "0.1.4",
774 | "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz",
775 | "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA=="
776 | },
777 | "node_modules/@types/mapbox__vector-tile": {
778 | "version": "1.3.4",
779 | "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz",
780 | "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==",
781 | "dependencies": {
782 | "@types/geojson": "*",
783 | "@types/mapbox__point-geometry": "*",
784 | "@types/pbf": "*"
785 | }
786 | },
787 | "node_modules/@types/pbf": {
788 | "version": "3.0.5",
789 | "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz",
790 | "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA=="
791 | },
792 | "node_modules/@types/supercluster": {
793 | "version": "7.1.3",
794 | "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz",
795 | "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
796 | "dependencies": {
797 | "@types/geojson": "*"
798 | }
799 | },
800 | "node_modules/acorn": {
801 | "version": "7.4.1",
802 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
803 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
804 | "dev": true,
805 | "bin": {
806 | "acorn": "bin/acorn"
807 | },
808 | "engines": {
809 | "node": ">=0.4.0"
810 | }
811 | },
812 | "node_modules/array-union": {
813 | "version": "2.1.0",
814 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
815 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
816 | "dev": true,
817 | "engines": {
818 | "node": ">=8"
819 | }
820 | },
821 | "node_modules/async": {
822 | "version": "3.2.6",
823 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
824 | "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
825 | "dev": true
826 | },
827 | "node_modules/bl": {
828 | "version": "2.2.1",
829 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
830 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
831 | "dev": true,
832 | "dependencies": {
833 | "readable-stream": "^2.3.5",
834 | "safe-buffer": "^5.1.1"
835 | }
836 | },
837 | "node_modules/braces": {
838 | "version": "3.0.3",
839 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
840 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
841 | "dev": true,
842 | "dependencies": {
843 | "fill-range": "^7.1.1"
844 | },
845 | "engines": {
846 | "node": ">=8"
847 | }
848 | },
849 | "node_modules/buffer-from": {
850 | "version": "1.1.2",
851 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
852 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
853 | "dev": true
854 | },
855 | "node_modules/chroma-js": {
856 | "version": "3.1.2",
857 | "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-3.1.2.tgz",
858 | "integrity": "sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg=="
859 | },
860 | "node_modules/colormap": {
861 | "version": "2.3.2",
862 | "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.3.2.tgz",
863 | "integrity": "sha512-jDOjaoEEmA9AgA11B/jCSAvYE95r3wRoAyTf3LEHGiUVlNHJaL1mRkf5AyLSpQBVGfTEPwGEqCIzL+kgr2WgNA==",
864 | "dependencies": {
865 | "lerp": "^1.0.3"
866 | }
867 | },
868 | "node_modules/commander": {
869 | "version": "2.20.3",
870 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
871 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
872 | "dev": true
873 | },
874 | "node_modules/commondir": {
875 | "version": "1.0.1",
876 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
877 | "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
878 | "dev": true
879 | },
880 | "node_modules/concat-stream": {
881 | "version": "1.6.2",
882 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
883 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
884 | "dev": true,
885 | "engines": [
886 | "node >= 0.8"
887 | ],
888 | "dependencies": {
889 | "buffer-from": "^1.0.0",
890 | "inherits": "^2.0.3",
891 | "readable-stream": "^2.2.2",
892 | "typedarray": "^0.0.6"
893 | }
894 | },
895 | "node_modules/core-util-is": {
896 | "version": "1.0.3",
897 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
898 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
899 | "dev": true
900 | },
901 | "node_modules/dir-glob": {
902 | "version": "3.0.1",
903 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
904 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
905 | "dev": true,
906 | "dependencies": {
907 | "path-type": "^4.0.0"
908 | },
909 | "engines": {
910 | "node": ">=8"
911 | }
912 | },
913 | "node_modules/duplexify": {
914 | "version": "3.7.1",
915 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
916 | "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
917 | "dev": true,
918 | "dependencies": {
919 | "end-of-stream": "^1.0.0",
920 | "inherits": "^2.0.1",
921 | "readable-stream": "^2.0.0",
922 | "stream-shift": "^1.0.0"
923 | }
924 | },
925 | "node_modules/earcut": {
926 | "version": "3.0.0",
927 | "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.0.tgz",
928 | "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg=="
929 | },
930 | "node_modules/email-addresses": {
931 | "version": "5.0.0",
932 | "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
933 | "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
934 | "dev": true
935 | },
936 | "node_modules/end-of-stream": {
937 | "version": "1.4.4",
938 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
939 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
940 | "dev": true,
941 | "dependencies": {
942 | "once": "^1.4.0"
943 | }
944 | },
945 | "node_modules/esbuild": {
946 | "version": "0.21.5",
947 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
948 | "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
949 | "dev": true,
950 | "hasInstallScript": true,
951 | "bin": {
952 | "esbuild": "bin/esbuild"
953 | },
954 | "engines": {
955 | "node": ">=12"
956 | },
957 | "optionalDependencies": {
958 | "@esbuild/aix-ppc64": "0.21.5",
959 | "@esbuild/android-arm": "0.21.5",
960 | "@esbuild/android-arm64": "0.21.5",
961 | "@esbuild/android-x64": "0.21.5",
962 | "@esbuild/darwin-arm64": "0.21.5",
963 | "@esbuild/darwin-x64": "0.21.5",
964 | "@esbuild/freebsd-arm64": "0.21.5",
965 | "@esbuild/freebsd-x64": "0.21.5",
966 | "@esbuild/linux-arm": "0.21.5",
967 | "@esbuild/linux-arm64": "0.21.5",
968 | "@esbuild/linux-ia32": "0.21.5",
969 | "@esbuild/linux-loong64": "0.21.5",
970 | "@esbuild/linux-mips64el": "0.21.5",
971 | "@esbuild/linux-ppc64": "0.21.5",
972 | "@esbuild/linux-riscv64": "0.21.5",
973 | "@esbuild/linux-s390x": "0.21.5",
974 | "@esbuild/linux-x64": "0.21.5",
975 | "@esbuild/netbsd-x64": "0.21.5",
976 | "@esbuild/openbsd-x64": "0.21.5",
977 | "@esbuild/sunos-x64": "0.21.5",
978 | "@esbuild/win32-arm64": "0.21.5",
979 | "@esbuild/win32-ia32": "0.21.5",
980 | "@esbuild/win32-x64": "0.21.5"
981 | }
982 | },
983 | "node_modules/escape-string-regexp": {
984 | "version": "1.0.5",
985 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
986 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
987 | "dev": true,
988 | "engines": {
989 | "node": ">=0.8.0"
990 | }
991 | },
992 | "node_modules/escodegen": {
993 | "version": "2.1.0",
994 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
995 | "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
996 | "dev": true,
997 | "dependencies": {
998 | "esprima": "^4.0.1",
999 | "estraverse": "^5.2.0",
1000 | "esutils": "^2.0.2"
1001 | },
1002 | "bin": {
1003 | "escodegen": "bin/escodegen.js",
1004 | "esgenerate": "bin/esgenerate.js"
1005 | },
1006 | "engines": {
1007 | "node": ">=6.0"
1008 | },
1009 | "optionalDependencies": {
1010 | "source-map": "~0.6.1"
1011 | }
1012 | },
1013 | "node_modules/esprima": {
1014 | "version": "4.0.1",
1015 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
1016 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
1017 | "dev": true,
1018 | "bin": {
1019 | "esparse": "bin/esparse.js",
1020 | "esvalidate": "bin/esvalidate.js"
1021 | },
1022 | "engines": {
1023 | "node": ">=4"
1024 | }
1025 | },
1026 | "node_modules/estraverse": {
1027 | "version": "5.3.0",
1028 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
1029 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
1030 | "dev": true,
1031 | "engines": {
1032 | "node": ">=4.0"
1033 | }
1034 | },
1035 | "node_modules/esutils": {
1036 | "version": "2.0.3",
1037 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
1038 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
1039 | "dev": true,
1040 | "engines": {
1041 | "node": ">=0.10.0"
1042 | }
1043 | },
1044 | "node_modules/events": {
1045 | "version": "3.3.0",
1046 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
1047 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
1048 | "dev": true,
1049 | "engines": {
1050 | "node": ">=0.8.x"
1051 | }
1052 | },
1053 | "node_modules/falafel": {
1054 | "version": "2.2.5",
1055 | "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.5.tgz",
1056 | "integrity": "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==",
1057 | "dev": true,
1058 | "dependencies": {
1059 | "acorn": "^7.1.1",
1060 | "isarray": "^2.0.1"
1061 | },
1062 | "engines": {
1063 | "node": ">=0.4.0"
1064 | }
1065 | },
1066 | "node_modules/fast-glob": {
1067 | "version": "3.3.3",
1068 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
1069 | "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
1070 | "dev": true,
1071 | "dependencies": {
1072 | "@nodelib/fs.stat": "^2.0.2",
1073 | "@nodelib/fs.walk": "^1.2.3",
1074 | "glob-parent": "^5.1.2",
1075 | "merge2": "^1.3.0",
1076 | "micromatch": "^4.0.8"
1077 | },
1078 | "engines": {
1079 | "node": ">=8.6.0"
1080 | }
1081 | },
1082 | "node_modules/fastq": {
1083 | "version": "1.19.1",
1084 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
1085 | "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
1086 | "dev": true,
1087 | "dependencies": {
1088 | "reusify": "^1.0.4"
1089 | }
1090 | },
1091 | "node_modules/filename-reserved-regex": {
1092 | "version": "2.0.0",
1093 | "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
1094 | "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
1095 | "dev": true,
1096 | "engines": {
1097 | "node": ">=4"
1098 | }
1099 | },
1100 | "node_modules/filenamify": {
1101 | "version": "4.3.0",
1102 | "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
1103 | "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
1104 | "dev": true,
1105 | "dependencies": {
1106 | "filename-reserved-regex": "^2.0.0",
1107 | "strip-outer": "^1.0.1",
1108 | "trim-repeated": "^1.0.0"
1109 | },
1110 | "engines": {
1111 | "node": ">=8"
1112 | },
1113 | "funding": {
1114 | "url": "https://github.com/sponsors/sindresorhus"
1115 | }
1116 | },
1117 | "node_modules/fill-range": {
1118 | "version": "7.1.1",
1119 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
1120 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
1121 | "dev": true,
1122 | "dependencies": {
1123 | "to-regex-range": "^5.0.1"
1124 | },
1125 | "engines": {
1126 | "node": ">=8"
1127 | }
1128 | },
1129 | "node_modules/find-cache-dir": {
1130 | "version": "3.3.2",
1131 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
1132 | "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
1133 | "dev": true,
1134 | "dependencies": {
1135 | "commondir": "^1.0.1",
1136 | "make-dir": "^3.0.2",
1137 | "pkg-dir": "^4.1.0"
1138 | },
1139 | "engines": {
1140 | "node": ">=8"
1141 | },
1142 | "funding": {
1143 | "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
1144 | }
1145 | },
1146 | "node_modules/find-up": {
1147 | "version": "4.1.0",
1148 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
1149 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
1150 | "dev": true,
1151 | "dependencies": {
1152 | "locate-path": "^5.0.0",
1153 | "path-exists": "^4.0.0"
1154 | },
1155 | "engines": {
1156 | "node": ">=8"
1157 | }
1158 | },
1159 | "node_modules/from2": {
1160 | "version": "2.3.0",
1161 | "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
1162 | "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
1163 | "dev": true,
1164 | "dependencies": {
1165 | "inherits": "^2.0.1",
1166 | "readable-stream": "^2.0.0"
1167 | }
1168 | },
1169 | "node_modules/fs-extra": {
1170 | "version": "11.3.0",
1171 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
1172 | "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
1173 | "dev": true,
1174 | "dependencies": {
1175 | "graceful-fs": "^4.2.0",
1176 | "jsonfile": "^6.0.1",
1177 | "universalify": "^2.0.0"
1178 | },
1179 | "engines": {
1180 | "node": ">=14.14"
1181 | }
1182 | },
1183 | "node_modules/fsevents": {
1184 | "version": "2.3.3",
1185 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1186 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1187 | "dev": true,
1188 | "hasInstallScript": true,
1189 | "optional": true,
1190 | "os": [
1191 | "darwin"
1192 | ],
1193 | "engines": {
1194 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1195 | }
1196 | },
1197 | "node_modules/function-bind": {
1198 | "version": "1.1.2",
1199 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
1200 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
1201 | "dev": true,
1202 | "funding": {
1203 | "url": "https://github.com/sponsors/ljharb"
1204 | }
1205 | },
1206 | "node_modules/geojson-vt": {
1207 | "version": "4.0.2",
1208 | "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz",
1209 | "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="
1210 | },
1211 | "node_modules/get-stream": {
1212 | "version": "6.0.1",
1213 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
1214 | "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
1215 | "engines": {
1216 | "node": ">=10"
1217 | },
1218 | "funding": {
1219 | "url": "https://github.com/sponsors/sindresorhus"
1220 | }
1221 | },
1222 | "node_modules/gh-pages": {
1223 | "version": "6.3.0",
1224 | "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz",
1225 | "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==",
1226 | "dev": true,
1227 | "dependencies": {
1228 | "async": "^3.2.4",
1229 | "commander": "^13.0.0",
1230 | "email-addresses": "^5.0.0",
1231 | "filenamify": "^4.3.0",
1232 | "find-cache-dir": "^3.3.1",
1233 | "fs-extra": "^11.1.1",
1234 | "globby": "^11.1.0"
1235 | },
1236 | "bin": {
1237 | "gh-pages": "bin/gh-pages.js",
1238 | "gh-pages-clean": "bin/gh-pages-clean.js"
1239 | },
1240 | "engines": {
1241 | "node": ">=10"
1242 | }
1243 | },
1244 | "node_modules/gh-pages/node_modules/commander": {
1245 | "version": "13.1.0",
1246 | "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
1247 | "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
1248 | "dev": true,
1249 | "engines": {
1250 | "node": ">=18"
1251 | }
1252 | },
1253 | "node_modules/gl-matrix": {
1254 | "version": "3.4.3",
1255 | "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
1256 | "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
1257 | },
1258 | "node_modules/glob-parent": {
1259 | "version": "5.1.2",
1260 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
1261 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
1262 | "dev": true,
1263 | "dependencies": {
1264 | "is-glob": "^4.0.1"
1265 | },
1266 | "engines": {
1267 | "node": ">= 6"
1268 | }
1269 | },
1270 | "node_modules/global-prefix": {
1271 | "version": "4.0.0",
1272 | "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz",
1273 | "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==",
1274 | "dependencies": {
1275 | "ini": "^4.1.3",
1276 | "kind-of": "^6.0.3",
1277 | "which": "^4.0.0"
1278 | },
1279 | "engines": {
1280 | "node": ">=16"
1281 | }
1282 | },
1283 | "node_modules/globby": {
1284 | "version": "11.1.0",
1285 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
1286 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
1287 | "dev": true,
1288 | "dependencies": {
1289 | "array-union": "^2.1.0",
1290 | "dir-glob": "^3.0.1",
1291 | "fast-glob": "^3.2.9",
1292 | "ignore": "^5.2.0",
1293 | "merge2": "^1.4.1",
1294 | "slash": "^3.0.0"
1295 | },
1296 | "engines": {
1297 | "node": ">=10"
1298 | },
1299 | "funding": {
1300 | "url": "https://github.com/sponsors/sindresorhus"
1301 | }
1302 | },
1303 | "node_modules/glsl-colormap": {
1304 | "version": "1.0.1",
1305 | "resolved": "https://registry.npmjs.org/glsl-colormap/-/glsl-colormap-1.0.1.tgz",
1306 | "integrity": "sha512-wtq1MVdu8IF4GZQnw6NIRzit/S5elC5bFHJNNqvwcsbZrg2bybx+T0IrOfB86B4cC8sLMaCyY/N2PjmmvXD/xg=="
1307 | },
1308 | "node_modules/glsl-inject-defines": {
1309 | "version": "1.0.3",
1310 | "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz",
1311 | "integrity": "sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A==",
1312 | "dev": true,
1313 | "dependencies": {
1314 | "glsl-token-inject-block": "^1.0.0",
1315 | "glsl-token-string": "^1.0.1",
1316 | "glsl-tokenizer": "^2.0.2"
1317 | }
1318 | },
1319 | "node_modules/glsl-resolve": {
1320 | "version": "0.0.1",
1321 | "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz",
1322 | "integrity": "sha512-xxFNsfnhZTK9NBhzJjSBGX6IOqYpvBHxxmo+4vapiljyGNCY0Bekzn0firQkQrazK59c1hYxMDxYS8MDlhw4gA==",
1323 | "dev": true,
1324 | "dependencies": {
1325 | "resolve": "^0.6.1",
1326 | "xtend": "^2.1.2"
1327 | }
1328 | },
1329 | "node_modules/glsl-resolve/node_modules/resolve": {
1330 | "version": "0.6.3",
1331 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz",
1332 | "integrity": "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==",
1333 | "dev": true
1334 | },
1335 | "node_modules/glsl-resolve/node_modules/xtend": {
1336 | "version": "2.2.0",
1337 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz",
1338 | "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==",
1339 | "dev": true,
1340 | "engines": {
1341 | "node": ">=0.4"
1342 | }
1343 | },
1344 | "node_modules/glsl-token-assignments": {
1345 | "version": "2.0.2",
1346 | "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz",
1347 | "integrity": "sha512-OwXrxixCyHzzA0U2g4btSNAyB2Dx8XrztY5aVUCjRSh4/D0WoJn8Qdps7Xub3sz6zE73W3szLrmWtQ7QMpeHEQ==",
1348 | "dev": true
1349 | },
1350 | "node_modules/glsl-token-defines": {
1351 | "version": "1.0.0",
1352 | "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz",
1353 | "integrity": "sha512-Vb5QMVeLjmOwvvOJuPNg3vnRlffscq2/qvIuTpMzuO/7s5kT+63iL6Dfo2FYLWbzuiycWpbC0/KV0biqFwHxaQ==",
1354 | "dev": true,
1355 | "dependencies": {
1356 | "glsl-tokenizer": "^2.0.0"
1357 | }
1358 | },
1359 | "node_modules/glsl-token-depth": {
1360 | "version": "1.1.2",
1361 | "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz",
1362 | "integrity": "sha512-eQnIBLc7vFf8axF9aoi/xW37LSWd2hCQr/3sZui8aBJnksq9C7zMeUYHVJWMhFzXrBU7fgIqni4EhXVW4/krpg==",
1363 | "dev": true
1364 | },
1365 | "node_modules/glsl-token-descope": {
1366 | "version": "1.0.2",
1367 | "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz",
1368 | "integrity": "sha512-kS2PTWkvi/YOeicVjXGgX5j7+8N7e56srNDEHDTVZ1dcESmbmpmgrnpjPcjxJjMxh56mSXYoFdZqb90gXkGjQw==",
1369 | "dev": true,
1370 | "dependencies": {
1371 | "glsl-token-assignments": "^2.0.0",
1372 | "glsl-token-depth": "^1.1.0",
1373 | "glsl-token-properties": "^1.0.0",
1374 | "glsl-token-scope": "^1.1.0"
1375 | }
1376 | },
1377 | "node_modules/glsl-token-inject-block": {
1378 | "version": "1.1.0",
1379 | "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz",
1380 | "integrity": "sha512-q/m+ukdUBuHCOtLhSr0uFb/qYQr4/oKrPSdIK2C4TD+qLaJvqM9wfXIF/OOBjuSA3pUoYHurVRNao6LTVVUPWA==",
1381 | "dev": true
1382 | },
1383 | "node_modules/glsl-token-properties": {
1384 | "version": "1.0.1",
1385 | "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz",
1386 | "integrity": "sha512-dSeW1cOIzbuUoYH0y+nxzwK9S9O3wsjttkq5ij9ZGw0OS41BirKJzzH48VLm8qLg+au6b0sINxGC0IrGwtQUcA==",
1387 | "dev": true
1388 | },
1389 | "node_modules/glsl-token-scope": {
1390 | "version": "1.1.2",
1391 | "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz",
1392 | "integrity": "sha512-YKyOMk1B/tz9BwYUdfDoHvMIYTGtVv2vbDSLh94PT4+f87z21FVdou1KNKgF+nECBTo0fJ20dpm0B1vZB1Q03A==",
1393 | "dev": true
1394 | },
1395 | "node_modules/glsl-token-string": {
1396 | "version": "1.0.1",
1397 | "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz",
1398 | "integrity": "sha512-1mtQ47Uxd47wrovl+T6RshKGkRRCYWhnELmkEcUAPALWGTFe2XZpH3r45XAwL2B6v+l0KNsCnoaZCSnhzKEksg==",
1399 | "dev": true
1400 | },
1401 | "node_modules/glsl-token-whitespace-trim": {
1402 | "version": "1.0.0",
1403 | "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz",
1404 | "integrity": "sha512-ZJtsPut/aDaUdLUNtmBYhaCmhIjpKNg7IgZSfX5wFReMc2vnj8zok+gB/3Quqs0TsBSX/fGnqUUYZDqyuc2xLQ==",
1405 | "dev": true
1406 | },
1407 | "node_modules/glsl-tokenizer": {
1408 | "version": "2.1.5",
1409 | "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz",
1410 | "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==",
1411 | "dev": true,
1412 | "dependencies": {
1413 | "through2": "^0.6.3"
1414 | }
1415 | },
1416 | "node_modules/glsl-tokenizer/node_modules/isarray": {
1417 | "version": "0.0.1",
1418 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
1419 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
1420 | "dev": true
1421 | },
1422 | "node_modules/glsl-tokenizer/node_modules/readable-stream": {
1423 | "version": "1.0.34",
1424 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
1425 | "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
1426 | "dev": true,
1427 | "dependencies": {
1428 | "core-util-is": "~1.0.0",
1429 | "inherits": "~2.0.1",
1430 | "isarray": "0.0.1",
1431 | "string_decoder": "~0.10.x"
1432 | }
1433 | },
1434 | "node_modules/glsl-tokenizer/node_modules/string_decoder": {
1435 | "version": "0.10.31",
1436 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
1437 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
1438 | "dev": true
1439 | },
1440 | "node_modules/glsl-tokenizer/node_modules/through2": {
1441 | "version": "0.6.5",
1442 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
1443 | "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==",
1444 | "dev": true,
1445 | "dependencies": {
1446 | "readable-stream": ">=1.0.33-1 <1.1.0-0",
1447 | "xtend": ">=4.0.0 <4.1.0-0"
1448 | }
1449 | },
1450 | "node_modules/glslify": {
1451 | "version": "7.1.1",
1452 | "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.1.1.tgz",
1453 | "integrity": "sha512-bud98CJ6kGZcP9Yxcsi7Iz647wuDz3oN+IZsjCRi5X1PI7t/xPKeL0mOwXJjo+CRZMqvq0CkSJiywCcY7kVYog==",
1454 | "dev": true,
1455 | "dependencies": {
1456 | "bl": "^2.2.1",
1457 | "concat-stream": "^1.5.2",
1458 | "duplexify": "^3.4.5",
1459 | "falafel": "^2.1.0",
1460 | "from2": "^2.3.0",
1461 | "glsl-resolve": "0.0.1",
1462 | "glsl-token-whitespace-trim": "^1.0.0",
1463 | "glslify-bundle": "^5.0.0",
1464 | "glslify-deps": "^1.2.5",
1465 | "minimist": "^1.2.5",
1466 | "resolve": "^1.1.5",
1467 | "stack-trace": "0.0.9",
1468 | "static-eval": "^2.0.5",
1469 | "through2": "^2.0.1",
1470 | "xtend": "^4.0.0"
1471 | },
1472 | "bin": {
1473 | "glslify": "bin.js"
1474 | }
1475 | },
1476 | "node_modules/glslify-bundle": {
1477 | "version": "5.1.1",
1478 | "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz",
1479 | "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==",
1480 | "dev": true,
1481 | "dependencies": {
1482 | "glsl-inject-defines": "^1.0.1",
1483 | "glsl-token-defines": "^1.0.0",
1484 | "glsl-token-depth": "^1.1.1",
1485 | "glsl-token-descope": "^1.0.2",
1486 | "glsl-token-scope": "^1.1.1",
1487 | "glsl-token-string": "^1.0.1",
1488 | "glsl-token-whitespace-trim": "^1.0.0",
1489 | "glsl-tokenizer": "^2.0.2",
1490 | "murmurhash-js": "^1.0.0",
1491 | "shallow-copy": "0.0.1"
1492 | }
1493 | },
1494 | "node_modules/glslify-deps": {
1495 | "version": "1.3.2",
1496 | "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz",
1497 | "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==",
1498 | "dev": true,
1499 | "dependencies": {
1500 | "@choojs/findup": "^0.2.0",
1501 | "events": "^3.2.0",
1502 | "glsl-resolve": "0.0.1",
1503 | "glsl-tokenizer": "^2.0.0",
1504 | "graceful-fs": "^4.1.2",
1505 | "inherits": "^2.0.1",
1506 | "map-limit": "0.0.1",
1507 | "resolve": "^1.0.0"
1508 | }
1509 | },
1510 | "node_modules/graceful-fs": {
1511 | "version": "4.2.11",
1512 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
1513 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
1514 | "dev": true
1515 | },
1516 | "node_modules/hasown": {
1517 | "version": "2.0.2",
1518 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
1519 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
1520 | "dev": true,
1521 | "dependencies": {
1522 | "function-bind": "^1.1.2"
1523 | },
1524 | "engines": {
1525 | "node": ">= 0.4"
1526 | }
1527 | },
1528 | "node_modules/ieee754": {
1529 | "version": "1.2.1",
1530 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
1531 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
1532 | "funding": [
1533 | {
1534 | "type": "github",
1535 | "url": "https://github.com/sponsors/feross"
1536 | },
1537 | {
1538 | "type": "patreon",
1539 | "url": "https://www.patreon.com/feross"
1540 | },
1541 | {
1542 | "type": "consulting",
1543 | "url": "https://feross.org/support"
1544 | }
1545 | ]
1546 | },
1547 | "node_modules/ignore": {
1548 | "version": "5.3.2",
1549 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
1550 | "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
1551 | "dev": true,
1552 | "engines": {
1553 | "node": ">= 4"
1554 | }
1555 | },
1556 | "node_modules/inherits": {
1557 | "version": "2.0.4",
1558 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1559 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1560 | "dev": true
1561 | },
1562 | "node_modules/ini": {
1563 | "version": "4.1.3",
1564 | "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz",
1565 | "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
1566 | "engines": {
1567 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
1568 | }
1569 | },
1570 | "node_modules/is-core-module": {
1571 | "version": "2.15.1",
1572 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
1573 | "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
1574 | "dev": true,
1575 | "dependencies": {
1576 | "hasown": "^2.0.2"
1577 | },
1578 | "engines": {
1579 | "node": ">= 0.4"
1580 | },
1581 | "funding": {
1582 | "url": "https://github.com/sponsors/ljharb"
1583 | }
1584 | },
1585 | "node_modules/is-extglob": {
1586 | "version": "2.1.1",
1587 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1588 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
1589 | "dev": true,
1590 | "engines": {
1591 | "node": ">=0.10.0"
1592 | }
1593 | },
1594 | "node_modules/is-glob": {
1595 | "version": "4.0.3",
1596 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
1597 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1598 | "dev": true,
1599 | "dependencies": {
1600 | "is-extglob": "^2.1.1"
1601 | },
1602 | "engines": {
1603 | "node": ">=0.10.0"
1604 | }
1605 | },
1606 | "node_modules/is-number": {
1607 | "version": "7.0.0",
1608 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1609 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1610 | "dev": true,
1611 | "engines": {
1612 | "node": ">=0.12.0"
1613 | }
1614 | },
1615 | "node_modules/isarray": {
1616 | "version": "2.0.5",
1617 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
1618 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
1619 | "dev": true
1620 | },
1621 | "node_modules/isexe": {
1622 | "version": "3.1.1",
1623 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
1624 | "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
1625 | "engines": {
1626 | "node": ">=16"
1627 | }
1628 | },
1629 | "node_modules/json-stringify-pretty-compact": {
1630 | "version": "4.0.0",
1631 | "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
1632 | "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q=="
1633 | },
1634 | "node_modules/jsonfile": {
1635 | "version": "6.1.0",
1636 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
1637 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
1638 | "dev": true,
1639 | "dependencies": {
1640 | "universalify": "^2.0.0"
1641 | },
1642 | "optionalDependencies": {
1643 | "graceful-fs": "^4.1.6"
1644 | }
1645 | },
1646 | "node_modules/kdbush": {
1647 | "version": "4.0.2",
1648 | "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
1649 | "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="
1650 | },
1651 | "node_modules/kind-of": {
1652 | "version": "6.0.3",
1653 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
1654 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
1655 | "engines": {
1656 | "node": ">=0.10.0"
1657 | }
1658 | },
1659 | "node_modules/lerp": {
1660 | "version": "1.0.3",
1661 | "resolved": "https://registry.npmjs.org/lerp/-/lerp-1.0.3.tgz",
1662 | "integrity": "sha512-70Rh4rCkJDvwWiTsyZ1HmJGvnyfFah4m6iTux29XmasRiZPDBpT9Cfa4ai73+uLZxnlKruUS62jj2lb11wURiA=="
1663 | },
1664 | "node_modules/lil-gui": {
1665 | "version": "0.19.2",
1666 | "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.19.2.tgz",
1667 | "integrity": "sha512-nU8j4ND702ouGfQZoaTN4dfXxacvGOAVK0DtmZBVcUYUAeYQXLQAjAN50igMHiba3T5jZyKEjXZU+Ntm1Qs6ZQ=="
1668 | },
1669 | "node_modules/locate-path": {
1670 | "version": "5.0.0",
1671 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
1672 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
1673 | "dev": true,
1674 | "dependencies": {
1675 | "p-locate": "^4.1.0"
1676 | },
1677 | "engines": {
1678 | "node": ">=8"
1679 | }
1680 | },
1681 | "node_modules/lodash.debounce": {
1682 | "version": "4.0.8",
1683 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
1684 | "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
1685 | },
1686 | "node_modules/make-dir": {
1687 | "version": "3.1.0",
1688 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
1689 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
1690 | "dev": true,
1691 | "dependencies": {
1692 | "semver": "^6.0.0"
1693 | },
1694 | "engines": {
1695 | "node": ">=8"
1696 | },
1697 | "funding": {
1698 | "url": "https://github.com/sponsors/sindresorhus"
1699 | }
1700 | },
1701 | "node_modules/map-limit": {
1702 | "version": "0.0.1",
1703 | "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
1704 | "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==",
1705 | "dev": true,
1706 | "dependencies": {
1707 | "once": "~1.3.0"
1708 | }
1709 | },
1710 | "node_modules/map-limit/node_modules/once": {
1711 | "version": "1.3.3",
1712 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
1713 | "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==",
1714 | "dev": true,
1715 | "dependencies": {
1716 | "wrappy": "1"
1717 | }
1718 | },
1719 | "node_modules/maplibre-gl": {
1720 | "version": "4.7.1",
1721 | "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz",
1722 | "integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==",
1723 | "dependencies": {
1724 | "@mapbox/geojson-rewind": "^0.5.2",
1725 | "@mapbox/jsonlint-lines-primitives": "^2.0.2",
1726 | "@mapbox/point-geometry": "^0.1.0",
1727 | "@mapbox/tiny-sdf": "^2.0.6",
1728 | "@mapbox/unitbezier": "^0.0.1",
1729 | "@mapbox/vector-tile": "^1.3.1",
1730 | "@mapbox/whoots-js": "^3.1.0",
1731 | "@maplibre/maplibre-gl-style-spec": "^20.3.1",
1732 | "@types/geojson": "^7946.0.14",
1733 | "@types/geojson-vt": "3.2.5",
1734 | "@types/mapbox__point-geometry": "^0.1.4",
1735 | "@types/mapbox__vector-tile": "^1.3.4",
1736 | "@types/pbf": "^3.0.5",
1737 | "@types/supercluster": "^7.1.3",
1738 | "earcut": "^3.0.0",
1739 | "geojson-vt": "^4.0.2",
1740 | "gl-matrix": "^3.4.3",
1741 | "global-prefix": "^4.0.0",
1742 | "kdbush": "^4.0.2",
1743 | "murmurhash-js": "^1.0.0",
1744 | "pbf": "^3.3.0",
1745 | "potpack": "^2.0.0",
1746 | "quickselect": "^3.0.0",
1747 | "supercluster": "^8.0.1",
1748 | "tinyqueue": "^3.0.0",
1749 | "vt-pbf": "^3.1.3"
1750 | },
1751 | "engines": {
1752 | "node": ">=16.14.0",
1753 | "npm": ">=8.1.0"
1754 | },
1755 | "funding": {
1756 | "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
1757 | }
1758 | },
1759 | "node_modules/merge2": {
1760 | "version": "1.4.1",
1761 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
1762 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
1763 | "dev": true,
1764 | "engines": {
1765 | "node": ">= 8"
1766 | }
1767 | },
1768 | "node_modules/micromatch": {
1769 | "version": "4.0.8",
1770 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
1771 | "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
1772 | "dev": true,
1773 | "dependencies": {
1774 | "braces": "^3.0.3",
1775 | "picomatch": "^2.3.1"
1776 | },
1777 | "engines": {
1778 | "node": ">=8.6"
1779 | }
1780 | },
1781 | "node_modules/minimist": {
1782 | "version": "1.2.8",
1783 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
1784 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
1785 | "funding": {
1786 | "url": "https://github.com/sponsors/ljharb"
1787 | }
1788 | },
1789 | "node_modules/murmurhash-js": {
1790 | "version": "1.0.0",
1791 | "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
1792 | "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="
1793 | },
1794 | "node_modules/nanoid": {
1795 | "version": "3.3.7",
1796 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
1797 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
1798 | "dev": true,
1799 | "funding": [
1800 | {
1801 | "type": "github",
1802 | "url": "https://github.com/sponsors/ai"
1803 | }
1804 | ],
1805 | "bin": {
1806 | "nanoid": "bin/nanoid.cjs"
1807 | },
1808 | "engines": {
1809 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1810 | }
1811 | },
1812 | "node_modules/once": {
1813 | "version": "1.4.0",
1814 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1815 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1816 | "dev": true,
1817 | "dependencies": {
1818 | "wrappy": "1"
1819 | }
1820 | },
1821 | "node_modules/p-limit": {
1822 | "version": "2.3.0",
1823 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
1824 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
1825 | "dev": true,
1826 | "dependencies": {
1827 | "p-try": "^2.0.0"
1828 | },
1829 | "engines": {
1830 | "node": ">=6"
1831 | },
1832 | "funding": {
1833 | "url": "https://github.com/sponsors/sindresorhus"
1834 | }
1835 | },
1836 | "node_modules/p-locate": {
1837 | "version": "4.1.0",
1838 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
1839 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
1840 | "dev": true,
1841 | "dependencies": {
1842 | "p-limit": "^2.2.0"
1843 | },
1844 | "engines": {
1845 | "node": ">=8"
1846 | }
1847 | },
1848 | "node_modules/p-try": {
1849 | "version": "2.2.0",
1850 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
1851 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
1852 | "dev": true,
1853 | "engines": {
1854 | "node": ">=6"
1855 | }
1856 | },
1857 | "node_modules/path-exists": {
1858 | "version": "4.0.0",
1859 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
1860 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
1861 | "dev": true,
1862 | "engines": {
1863 | "node": ">=8"
1864 | }
1865 | },
1866 | "node_modules/path-parse": {
1867 | "version": "1.0.7",
1868 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1869 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1870 | "dev": true
1871 | },
1872 | "node_modules/path-type": {
1873 | "version": "4.0.0",
1874 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
1875 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
1876 | "dev": true,
1877 | "engines": {
1878 | "node": ">=8"
1879 | }
1880 | },
1881 | "node_modules/pbf": {
1882 | "version": "3.3.0",
1883 | "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz",
1884 | "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==",
1885 | "dependencies": {
1886 | "ieee754": "^1.1.12",
1887 | "resolve-protobuf-schema": "^2.1.0"
1888 | },
1889 | "bin": {
1890 | "pbf": "bin/pbf"
1891 | }
1892 | },
1893 | "node_modules/picocolors": {
1894 | "version": "1.1.1",
1895 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1896 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1897 | "dev": true
1898 | },
1899 | "node_modules/picomatch": {
1900 | "version": "2.3.1",
1901 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1902 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1903 | "dev": true,
1904 | "engines": {
1905 | "node": ">=8.6"
1906 | },
1907 | "funding": {
1908 | "url": "https://github.com/sponsors/jonschlinkert"
1909 | }
1910 | },
1911 | "node_modules/pkg-dir": {
1912 | "version": "4.2.0",
1913 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
1914 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
1915 | "dev": true,
1916 | "dependencies": {
1917 | "find-up": "^4.0.0"
1918 | },
1919 | "engines": {
1920 | "node": ">=8"
1921 | }
1922 | },
1923 | "node_modules/postcss": {
1924 | "version": "8.4.47",
1925 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
1926 | "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
1927 | "dev": true,
1928 | "funding": [
1929 | {
1930 | "type": "opencollective",
1931 | "url": "https://opencollective.com/postcss/"
1932 | },
1933 | {
1934 | "type": "tidelift",
1935 | "url": "https://tidelift.com/funding/github/npm/postcss"
1936 | },
1937 | {
1938 | "type": "github",
1939 | "url": "https://github.com/sponsors/ai"
1940 | }
1941 | ],
1942 | "dependencies": {
1943 | "nanoid": "^3.3.7",
1944 | "picocolors": "^1.1.0",
1945 | "source-map-js": "^1.2.1"
1946 | },
1947 | "engines": {
1948 | "node": "^10 || ^12 || >=14"
1949 | }
1950 | },
1951 | "node_modules/potpack": {
1952 | "version": "2.0.0",
1953 | "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz",
1954 | "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw=="
1955 | },
1956 | "node_modules/process-nextick-args": {
1957 | "version": "2.0.1",
1958 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
1959 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
1960 | "dev": true
1961 | },
1962 | "node_modules/protocol-buffers-schema": {
1963 | "version": "3.6.0",
1964 | "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
1965 | "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
1966 | },
1967 | "node_modules/queue-microtask": {
1968 | "version": "1.2.3",
1969 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
1970 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
1971 | "dev": true,
1972 | "funding": [
1973 | {
1974 | "type": "github",
1975 | "url": "https://github.com/sponsors/feross"
1976 | },
1977 | {
1978 | "type": "patreon",
1979 | "url": "https://www.patreon.com/feross"
1980 | },
1981 | {
1982 | "type": "consulting",
1983 | "url": "https://feross.org/support"
1984 | }
1985 | ]
1986 | },
1987 | "node_modules/quickselect": {
1988 | "version": "3.0.0",
1989 | "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
1990 | "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="
1991 | },
1992 | "node_modules/readable-stream": {
1993 | "version": "2.3.8",
1994 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
1995 | "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
1996 | "dev": true,
1997 | "dependencies": {
1998 | "core-util-is": "~1.0.0",
1999 | "inherits": "~2.0.3",
2000 | "isarray": "~1.0.0",
2001 | "process-nextick-args": "~2.0.0",
2002 | "safe-buffer": "~5.1.1",
2003 | "string_decoder": "~1.1.1",
2004 | "util-deprecate": "~1.0.1"
2005 | }
2006 | },
2007 | "node_modules/readable-stream/node_modules/isarray": {
2008 | "version": "1.0.0",
2009 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
2010 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
2011 | "dev": true
2012 | },
2013 | "node_modules/readable-stream/node_modules/safe-buffer": {
2014 | "version": "5.1.2",
2015 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
2016 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
2017 | "dev": true
2018 | },
2019 | "node_modules/resolve": {
2020 | "version": "1.22.8",
2021 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
2022 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
2023 | "dev": true,
2024 | "dependencies": {
2025 | "is-core-module": "^2.13.0",
2026 | "path-parse": "^1.0.7",
2027 | "supports-preserve-symlinks-flag": "^1.0.0"
2028 | },
2029 | "bin": {
2030 | "resolve": "bin/resolve"
2031 | },
2032 | "funding": {
2033 | "url": "https://github.com/sponsors/ljharb"
2034 | }
2035 | },
2036 | "node_modules/resolve-protobuf-schema": {
2037 | "version": "2.1.0",
2038 | "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
2039 | "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
2040 | "dependencies": {
2041 | "protocol-buffers-schema": "^3.3.1"
2042 | }
2043 | },
2044 | "node_modules/reusify": {
2045 | "version": "1.1.0",
2046 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
2047 | "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
2048 | "dev": true,
2049 | "engines": {
2050 | "iojs": ">=1.0.0",
2051 | "node": ">=0.10.0"
2052 | }
2053 | },
2054 | "node_modules/rollup": {
2055 | "version": "4.24.0",
2056 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
2057 | "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
2058 | "dev": true,
2059 | "dependencies": {
2060 | "@types/estree": "1.0.6"
2061 | },
2062 | "bin": {
2063 | "rollup": "dist/bin/rollup"
2064 | },
2065 | "engines": {
2066 | "node": ">=18.0.0",
2067 | "npm": ">=8.0.0"
2068 | },
2069 | "optionalDependencies": {
2070 | "@rollup/rollup-android-arm-eabi": "4.24.0",
2071 | "@rollup/rollup-android-arm64": "4.24.0",
2072 | "@rollup/rollup-darwin-arm64": "4.24.0",
2073 | "@rollup/rollup-darwin-x64": "4.24.0",
2074 | "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
2075 | "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
2076 | "@rollup/rollup-linux-arm64-gnu": "4.24.0",
2077 | "@rollup/rollup-linux-arm64-musl": "4.24.0",
2078 | "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
2079 | "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
2080 | "@rollup/rollup-linux-s390x-gnu": "4.24.0",
2081 | "@rollup/rollup-linux-x64-gnu": "4.24.0",
2082 | "@rollup/rollup-linux-x64-musl": "4.24.0",
2083 | "@rollup/rollup-win32-arm64-msvc": "4.24.0",
2084 | "@rollup/rollup-win32-ia32-msvc": "4.24.0",
2085 | "@rollup/rollup-win32-x64-msvc": "4.24.0",
2086 | "fsevents": "~2.3.2"
2087 | }
2088 | },
2089 | "node_modules/run-parallel": {
2090 | "version": "1.2.0",
2091 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
2092 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
2093 | "dev": true,
2094 | "funding": [
2095 | {
2096 | "type": "github",
2097 | "url": "https://github.com/sponsors/feross"
2098 | },
2099 | {
2100 | "type": "patreon",
2101 | "url": "https://www.patreon.com/feross"
2102 | },
2103 | {
2104 | "type": "consulting",
2105 | "url": "https://feross.org/support"
2106 | }
2107 | ],
2108 | "dependencies": {
2109 | "queue-microtask": "^1.2.2"
2110 | }
2111 | },
2112 | "node_modules/rw": {
2113 | "version": "1.3.3",
2114 | "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
2115 | "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
2116 | },
2117 | "node_modules/safe-buffer": {
2118 | "version": "5.2.1",
2119 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
2120 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
2121 | "dev": true,
2122 | "funding": [
2123 | {
2124 | "type": "github",
2125 | "url": "https://github.com/sponsors/feross"
2126 | },
2127 | {
2128 | "type": "patreon",
2129 | "url": "https://www.patreon.com/feross"
2130 | },
2131 | {
2132 | "type": "consulting",
2133 | "url": "https://feross.org/support"
2134 | }
2135 | ]
2136 | },
2137 | "node_modules/semver": {
2138 | "version": "6.3.1",
2139 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
2140 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
2141 | "dev": true,
2142 | "bin": {
2143 | "semver": "bin/semver.js"
2144 | }
2145 | },
2146 | "node_modules/shallow-copy": {
2147 | "version": "0.0.1",
2148 | "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
2149 | "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==",
2150 | "dev": true
2151 | },
2152 | "node_modules/slash": {
2153 | "version": "3.0.0",
2154 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
2155 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
2156 | "dev": true,
2157 | "engines": {
2158 | "node": ">=8"
2159 | }
2160 | },
2161 | "node_modules/source-map": {
2162 | "version": "0.6.1",
2163 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
2164 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
2165 | "dev": true,
2166 | "optional": true,
2167 | "engines": {
2168 | "node": ">=0.10.0"
2169 | }
2170 | },
2171 | "node_modules/source-map-js": {
2172 | "version": "1.2.1",
2173 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
2174 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
2175 | "dev": true,
2176 | "engines": {
2177 | "node": ">=0.10.0"
2178 | }
2179 | },
2180 | "node_modules/stack-trace": {
2181 | "version": "0.0.9",
2182 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
2183 | "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==",
2184 | "dev": true,
2185 | "engines": {
2186 | "node": "*"
2187 | }
2188 | },
2189 | "node_modules/static-eval": {
2190 | "version": "2.1.1",
2191 | "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.1.tgz",
2192 | "integrity": "sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==",
2193 | "dev": true,
2194 | "dependencies": {
2195 | "escodegen": "^2.1.0"
2196 | }
2197 | },
2198 | "node_modules/stream-shift": {
2199 | "version": "1.0.3",
2200 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
2201 | "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
2202 | "dev": true
2203 | },
2204 | "node_modules/string_decoder": {
2205 | "version": "1.1.1",
2206 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
2207 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
2208 | "dev": true,
2209 | "dependencies": {
2210 | "safe-buffer": "~5.1.0"
2211 | }
2212 | },
2213 | "node_modules/string_decoder/node_modules/safe-buffer": {
2214 | "version": "5.1.2",
2215 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
2216 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
2217 | "dev": true
2218 | },
2219 | "node_modules/strip-outer": {
2220 | "version": "1.0.1",
2221 | "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
2222 | "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
2223 | "dev": true,
2224 | "dependencies": {
2225 | "escape-string-regexp": "^1.0.2"
2226 | },
2227 | "engines": {
2228 | "node": ">=0.10.0"
2229 | }
2230 | },
2231 | "node_modules/supercluster": {
2232 | "version": "8.0.1",
2233 | "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
2234 | "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
2235 | "dependencies": {
2236 | "kdbush": "^4.0.2"
2237 | }
2238 | },
2239 | "node_modules/supports-preserve-symlinks-flag": {
2240 | "version": "1.0.0",
2241 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
2242 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
2243 | "dev": true,
2244 | "engines": {
2245 | "node": ">= 0.4"
2246 | },
2247 | "funding": {
2248 | "url": "https://github.com/sponsors/ljharb"
2249 | }
2250 | },
2251 | "node_modules/through2": {
2252 | "version": "2.0.5",
2253 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
2254 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
2255 | "dev": true,
2256 | "dependencies": {
2257 | "readable-stream": "~2.3.6",
2258 | "xtend": "~4.0.1"
2259 | }
2260 | },
2261 | "node_modules/tinyqueue": {
2262 | "version": "3.0.0",
2263 | "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz",
2264 | "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g=="
2265 | },
2266 | "node_modules/to-regex-range": {
2267 | "version": "5.0.1",
2268 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
2269 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
2270 | "dev": true,
2271 | "dependencies": {
2272 | "is-number": "^7.0.0"
2273 | },
2274 | "engines": {
2275 | "node": ">=8.0"
2276 | }
2277 | },
2278 | "node_modules/trim-repeated": {
2279 | "version": "1.0.0",
2280 | "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
2281 | "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
2282 | "dev": true,
2283 | "dependencies": {
2284 | "escape-string-regexp": "^1.0.2"
2285 | },
2286 | "engines": {
2287 | "node": ">=0.10.0"
2288 | }
2289 | },
2290 | "node_modules/typedarray": {
2291 | "version": "0.0.6",
2292 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
2293 | "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
2294 | "dev": true
2295 | },
2296 | "node_modules/typescript": {
2297 | "version": "5.6.3",
2298 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
2299 | "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
2300 | "dev": true,
2301 | "bin": {
2302 | "tsc": "bin/tsc",
2303 | "tsserver": "bin/tsserver"
2304 | },
2305 | "engines": {
2306 | "node": ">=14.17"
2307 | }
2308 | },
2309 | "node_modules/universalify": {
2310 | "version": "2.0.1",
2311 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
2312 | "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
2313 | "dev": true,
2314 | "engines": {
2315 | "node": ">= 10.0.0"
2316 | }
2317 | },
2318 | "node_modules/util-deprecate": {
2319 | "version": "1.0.2",
2320 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
2321 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
2322 | "dev": true
2323 | },
2324 | "node_modules/vite": {
2325 | "version": "5.4.9",
2326 | "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
2327 | "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
2328 | "dev": true,
2329 | "dependencies": {
2330 | "esbuild": "^0.21.3",
2331 | "postcss": "^8.4.43",
2332 | "rollup": "^4.20.0"
2333 | },
2334 | "bin": {
2335 | "vite": "bin/vite.js"
2336 | },
2337 | "engines": {
2338 | "node": "^18.0.0 || >=20.0.0"
2339 | },
2340 | "funding": {
2341 | "url": "https://github.com/vitejs/vite?sponsor=1"
2342 | },
2343 | "optionalDependencies": {
2344 | "fsevents": "~2.3.3"
2345 | },
2346 | "peerDependencies": {
2347 | "@types/node": "^18.0.0 || >=20.0.0",
2348 | "less": "*",
2349 | "lightningcss": "^1.21.0",
2350 | "sass": "*",
2351 | "sass-embedded": "*",
2352 | "stylus": "*",
2353 | "sugarss": "*",
2354 | "terser": "^5.4.0"
2355 | },
2356 | "peerDependenciesMeta": {
2357 | "@types/node": {
2358 | "optional": true
2359 | },
2360 | "less": {
2361 | "optional": true
2362 | },
2363 | "lightningcss": {
2364 | "optional": true
2365 | },
2366 | "sass": {
2367 | "optional": true
2368 | },
2369 | "sass-embedded": {
2370 | "optional": true
2371 | },
2372 | "stylus": {
2373 | "optional": true
2374 | },
2375 | "sugarss": {
2376 | "optional": true
2377 | },
2378 | "terser": {
2379 | "optional": true
2380 | }
2381 | }
2382 | },
2383 | "node_modules/vt-pbf": {
2384 | "version": "3.1.3",
2385 | "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
2386 | "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
2387 | "dependencies": {
2388 | "@mapbox/point-geometry": "0.1.0",
2389 | "@mapbox/vector-tile": "^1.3.1",
2390 | "pbf": "^3.2.1"
2391 | }
2392 | },
2393 | "node_modules/which": {
2394 | "version": "4.0.0",
2395 | "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
2396 | "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
2397 | "dependencies": {
2398 | "isexe": "^3.1.1"
2399 | },
2400 | "bin": {
2401 | "node-which": "bin/which.js"
2402 | },
2403 | "engines": {
2404 | "node": "^16.13.0 || >=18.0.0"
2405 | }
2406 | },
2407 | "node_modules/wrappy": {
2408 | "version": "1.0.2",
2409 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2410 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
2411 | "dev": true
2412 | },
2413 | "node_modules/xtend": {
2414 | "version": "4.0.2",
2415 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
2416 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
2417 | "dev": true,
2418 | "engines": {
2419 | "node": ">=0.4"
2420 | }
2421 | }
2422 | }
2423 | }
2424 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "maplibre-terrain-tiles-viewer",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "deploy": "npm run build && gh-pages -d dist -m \"Updates --skip-ci\"",
10 | "preview": "vite preview"
11 | },
12 | "devDependencies": {
13 | "@types/chroma-js": "^2.4.4",
14 | "@types/colormap": "^2.3.4",
15 | "@types/lodash.debounce": "^4.0.9",
16 | "gh-pages": "^6.3.0",
17 | "glslify": "^7.1.1",
18 | "typescript": "^5.5.3",
19 | "vite": "^5.4.8"
20 | },
21 | "dependencies": {
22 | "chroma-js": "^3.1.2",
23 | "colormap": "^2.3.2",
24 | "glsl-colormap": "^1.0.1",
25 | "lil-gui": "^0.19.2",
26 | "lodash.debounce": "^4.0.8",
27 | "maplibre-gl": "^4.7.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/magma-bg-pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forestacdev/maplibre-terrain-visualizer/dd058a6135738d9c2d1238761cb5924de1aa0eb3/public/magma-bg-pattern.jpg
--------------------------------------------------------------------------------
/public/water-bg-pattern-03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forestacdev/maplibre-terrain-visualizer/dd058a6135738d9c2d1238761cb5924de1aa0eb3/public/water-bg-pattern-03.jpg
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import './style.css'; // CSSファイルのimport
2 | import maplibregl from 'maplibre-gl';
3 | import type { RasterTileSource } from 'maplibre-gl';
4 | import 'maplibre-gl/dist/maplibre-gl.css';
5 | import { GUI } from 'lil-gui';
6 | import { demEntry, demLayers, isBBoxOverlapping, isColorMapParameter, isTextureParameter } from './utils';
7 | import { demProtocol } from './protocol/raster';
8 | import { terrainProtocol } from './protocol/terrain';
9 | import chroma from 'chroma-js';
10 | import colormap from 'colormap';
11 | import debounce from 'lodash.debounce';
12 | import { backgroundSources, tileOptions } from './utils';
13 |
14 | const protocol = demProtocol('webgl');
15 | maplibregl.addProtocol(protocol.protocolName, protocol.request);
16 |
17 | const protocol2 = terrainProtocol('terrain');
18 | maplibregl.addProtocol(protocol2.protocolName, protocol2.request);
19 |
20 | // 地図の表示
21 | const map = new maplibregl.Map({
22 | container: 'map',
23 | style: {
24 | version: 8,
25 | glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
26 | sources: {
27 | terrain: {
28 | type: 'raster-dem',
29 | tiles: [`${protocol2.protocolName}://${demEntry.url}?x={x}&y={y}&z={z}`],
30 | tileSize: 256,
31 | minzoom: demEntry.sourceMinZoom,
32 | maxzoom: demEntry.sourceMaxZoom,
33 | attribution: demEntry.attribution,
34 | bounds: demEntry.bbox,
35 | },
36 | background: {
37 | type: 'raster',
38 | tiles: ['https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png'],
39 | tileSize: 256,
40 | maxzoom: 18,
41 | attribution:
42 | 'MIERUNE Inc. © OpenMapTiles © OpenStreetMap contributors',
43 | },
44 | custom: {
45 | type: 'raster',
46 | tiles: [`${protocol.protocolName}://${demEntry.url}?x={x}&y={y}&z={z}`],
47 | tileSize: 256,
48 | minzoom: demEntry.sourceMinZoom,
49 | maxzoom: demEntry.sourceMaxZoom,
50 | attribution: demEntry.attribution,
51 | bounds: demEntry.bbox,
52 | },
53 | },
54 | layers: [
55 | {
56 | id: 'background_layer',
57 | source: 'background',
58 | type: 'raster',
59 | },
60 | {
61 | id: 'custom_layer',
62 | source: 'custom',
63 | type: 'raster',
64 | maxzoom: 24,
65 | },
66 | ],
67 | sky: {
68 | 'sky-color': '#2baeff',
69 | 'sky-horizon-blend': 0.5,
70 | 'horizon-color': '#ffffff',
71 | 'horizon-fog-blend': 0.5,
72 | 'fog-color': '#2222ff',
73 | 'fog-ground-blend': 0.5,
74 | 'atmosphere-blend': ['interpolate', ['linear'], ['zoom'], 0, 1, 10, 1, 12, 0],
75 | },
76 | },
77 | center: [138.3863, 35.3379],
78 | zoom: 10,
79 | maxPitch: 85,
80 | // hash: true,
81 | renderWorldCopies: false,
82 | });
83 |
84 | map.addControl(new maplibregl.NavigationControl(), 'top-right');
85 | // 現在地
86 | map.addControl(
87 | new maplibregl.GeolocateControl({
88 | positionOptions: {
89 | enableHighAccuracy: true,
90 | },
91 | trackUserLocation: true,
92 | }),
93 | 'top-right',
94 | );
95 |
96 | // スケールバーの追加
97 | map.addControl(
98 | new maplibregl.ScaleControl({
99 | maxWidth: 200, // スケールの最大幅
100 | unit: 'metric', // 単位
101 | }),
102 | 'bottom-left',
103 | );
104 |
105 | const reloadTiles = debounce(() => {
106 | // protocol.cancelAllRequests();
107 | const _source = map.getSource('custom') as RasterTileSource;
108 | _source.setTiles([`${protocol.protocolName}://${demEntry.url}?x={x}&y={y}&z={z}`]);
109 | }, 100);
110 |
111 | const resetDem = () => {
112 | protocol.cancelAllRequests();
113 | protocol.clearCache();
114 |
115 | map.removeLayer('custom_layer');
116 | map.removeSource('custom');
117 | map.addSource('custom', {
118 | type: 'raster',
119 | tiles: [`${protocol.protocolName}://${demEntry.url}?x={x}&y={y}&z={z}&demType=${demEntry.demType}&maxzoom=${demEntry.sourceMaxZoom}`],
120 | tileSize: 256,
121 | minzoom: demEntry.sourceMinZoom,
122 | maxzoom: demEntry.sourceMaxZoom,
123 | attribution: demEntry.attribution,
124 | bounds: demEntry.bbox,
125 | });
126 | map.addLayer({
127 | id: 'custom_layer',
128 | source: 'custom',
129 | type: 'raster',
130 | });
131 |
132 | const has3D = map.getTerrain();
133 | map.setTerrain(null);
134 | map.removeSource('terrain');
135 |
136 | protocol2.cancelAllRequests();
137 |
138 | map.addSource('terrain', {
139 | type: 'raster-dem',
140 | tiles: [`${protocol2.protocolName}://${demEntry.url}?demType=${demEntry.demType}`],
141 | tileSize: 256,
142 | minzoom: demEntry.sourceMinZoom,
143 | maxzoom: demEntry.sourceMaxZoom,
144 | attribution: demEntry.attribution,
145 | bounds: demEntry.bbox,
146 | });
147 |
148 | if (has3D) {
149 | map.setTerrain({
150 | source: 'terrain',
151 | exaggeration: 1,
152 | });
153 | }
154 |
155 | const bbox = demEntry.bbox;
156 | if (!bbox) return;
157 |
158 | const bounds = map.getBounds().toArray();
159 |
160 | const zoom = map.getZoom();
161 |
162 | if (!isBBoxOverlapping(demEntry.bbox, [...bounds[0], ...bounds[1]] as [number, number, number, number])) {
163 | map.fitBounds(bbox, {
164 | padding: 100,
165 | duration: 300,
166 | bearing: map.getBearing(),
167 | });
168 | }
169 |
170 | if (zoom < demEntry.sourceMinZoom || zoom > demEntry.sourceMaxZoom) {
171 | map.setZoom(demEntry.sourceMaxZoom - 1.5);
172 | }
173 | };
174 |
175 | const gui = new GUI({
176 | title: 'コントロール',
177 | container: document.getElementById('gui') as HTMLElement,
178 | width: window.innerWidth < 768 ? window.innerWidth - 50 : 350,
179 | });
180 |
181 | // 画面サイズに応じてGUIを折りたたむ
182 | if (window.innerWidth < 768) {
183 | gui.close();
184 | }
185 |
186 | gui.add(
187 | demEntry,
188 | 'name',
189 | demLayers.map((layer) => layer.name),
190 | )
191 | .name('地形データ')
192 | .onChange((value: string) => {
193 | const layer = demLayers.find((layer) => layer.name === value);
194 | if (layer) {
195 | demEntry.url = layer.tiles[0];
196 | demEntry.demType = layer.demType;
197 | demEntry.sourceMinZoom = layer.minzoom;
198 | demEntry.sourceMaxZoom = layer.maxzoom;
199 | demEntry.bbox = layer.bbox;
200 | demEntry.attribution = layer.attribution;
201 | resetDem();
202 | }
203 | });
204 |
205 | const createColors = (colorMap: string, reverse: boolean = false): string => {
206 | const options = {
207 | colormap: colorMap, // colormap でサポートされているカラースケール名
208 | nshades: 100, // 色の段階数
209 | format: 'hex', // 色のフォーマット ('hex', 'rgb', 'rgba' など)
210 | alpha: 1, // 透明度
211 | };
212 |
213 | const colorArray = colormap(options as any);
214 | if (reverse) {
215 | colorArray.reverse();
216 | }
217 |
218 | const scale = chroma.scale(colorArray as any).colors(100);
219 |
220 | // グラデーション用のカラーを文字列として連結
221 | return scale.map((color) => color).join(', ');
222 | };
223 |
224 | // コントロールの制御
225 | const enableAllControllers = (controllers: Array, activeController: any, value: boolean) => {
226 | controllers.forEach((controller) => {
227 | if (controller !== activeController) {
228 | value ? controller.show() : controller.hide();
229 | }
230 | });
231 | };
232 |
233 | // 背景地図の切り替え
234 | gui.add(
235 | {
236 | background: 'mierune_mono',
237 | },
238 | 'background',
239 | Object.keys(backgroundSources).map((name) => name),
240 | )
241 | .name('背景地図')
242 | .onChange((value: string) => {
243 | const sourceData = backgroundSources[value];
244 |
245 | map.removeLayer('background_layer');
246 | map.removeSource('background');
247 |
248 | map.addSource('background', {
249 | ...sourceData,
250 | });
251 | map.addLayer(
252 | {
253 | id: 'background_layer',
254 | source: 'background',
255 | type: 'raster',
256 | },
257 | 'custom_layer',
258 | );
259 | });
260 |
261 | gui.add(tileOptions.normalMapQuality, 'value', tileOptions.normalMapQuality.selection).name(tileOptions.normalMapQuality.name).onChange(reloadTiles);
262 |
263 | const terrainParams = {
264 | exaggeration: 1,
265 | };
266 |
267 | // 3D表示のコントロール
268 | gui.add({ value: false }, 'value')
269 | .name('3D表示')
270 | .onChange((value: boolean) => {
271 | if (value) {
272 | map.setTerrain({
273 | source: 'terrain',
274 | exaggeration: terrainParams.exaggeration,
275 | });
276 | if (map.getPitch() === 0) {
277 | map.easeTo({ pitch: 60 });
278 | }
279 | } else {
280 | map.setTerrain(null);
281 | map.easeTo({ pitch: 0 });
282 | }
283 | });
284 |
285 | // 各プロパティに対応するフォルダを作成
286 | Object.entries(demEntry.uniformsData).forEach(([_key, data]) => {
287 | let _folder: any;
288 | _folder = gui.addFolder(data.name);
289 |
290 | data.showMenu ? _folder.open() : _folder.close();
291 |
292 | // パラメータを保持しておくための配列
293 | const paramControllers: Array = [];
294 |
295 | Object.entries(data.option).forEach(([_prop, paramData]) => {
296 | const div = document.createElement('div');
297 | let controller: any;
298 |
299 | if (typeof paramData === 'boolean') {
300 | // _folder.add(paramData, prop).name(data.option[prop].name).onChange(reloadTiles);
301 | } else if (typeof paramData !== 'string' && typeof paramData !== 'boolean' && 'value' in paramData) {
302 | if (typeof paramData.value === 'boolean') {
303 | controller = _folder
304 | .add(paramData, 'value')
305 | .name(paramData.name)
306 | .onChange((value: boolean) => {
307 | reloadTiles();
308 | enableAllControllers(paramControllers, controller, value);
309 | });
310 | } else if (typeof paramData.value === 'number') {
311 | controller = _folder.add(paramData, 'value', paramData.min, paramData.max, paramData.step).name(paramData.name).onChange(reloadTiles);
312 | } else if (typeof paramData.value === 'string') {
313 | if (isColorMapParameter(paramData)) {
314 | controller = _folder
315 | .add(paramData, 'value', paramData.selection)
316 | .name(paramData.name)
317 | .onChange((value: string) => {
318 | div.style.background = `linear-gradient(to right, ${createColors(value, paramData.reverse)})`;
319 | reloadTiles();
320 | });
321 | const children = _folder.$children.querySelector('.option');
322 | if (children) {
323 | // 小要素の追加
324 | div.style.height = '20px';
325 | div.style.width = '300px';
326 | div.style.background = `linear-gradient(to right, ${createColors(paramData.value, paramData.reverse)})`;
327 | // 選択肢の追加
328 | children.appendChild(div);
329 | }
330 |
331 | // カラーランプの反転チェックボックスの追加
332 | const reverseController = _folder
333 | .add(paramData, 'reverse')
334 | .name('カラーランプの反転')
335 | .onChange(() => {
336 | div.style.background = `linear-gradient(to right, ${createColors(paramData.value, paramData.reverse)})`;
337 | reloadTiles();
338 | });
339 |
340 | paramControllers.push(reverseController);
341 | if (!data.showMenu) {
342 | reverseController.hide();
343 | }
344 | } else if (isTextureParameter(paramData)) {
345 | controller = _folder
346 | .add(paramData, 'value', paramData.selection)
347 | .name(paramData.name)
348 | .onChange(() => {
349 | reloadTiles();
350 | });
351 | } else {
352 | controller = _folder.addColor(paramData, 'value').name(paramData.name).onChange(reloadTiles);
353 | }
354 | }
355 | }
356 |
357 | // controller が存在する場合は配列に保持
358 | if (controller) {
359 | paramControllers.push(controller);
360 | if (!data.showMenu && controller.object.name !== '表示') {
361 | controller.hide();
362 | }
363 | }
364 | });
365 | });
366 |
367 | const other = gui.addFolder('その他').close();
368 |
369 | other
370 | .add(
371 | {
372 | export: () => {
373 | // レンダリングが完了してからエクスポート
374 | map.once('render', () => {
375 | const canvas = map.getCanvas();
376 | const link = document.createElement('a');
377 | link.download = 'map.png';
378 | link.href = canvas.toDataURL('image/png');
379 | link.click();
380 | });
381 |
382 | map.triggerRepaint();
383 | },
384 | },
385 | 'export',
386 | )
387 | .name('PNGエクスポート');
388 |
389 | other
390 | .add(
391 | {
392 | link: () => {
393 | window.open('https://github.com/forestacdev/maplibre-terrain-visualizer', '_blank', 'noopener,noreferrer'); // ここにリンクを追加
394 | },
395 | },
396 | 'link',
397 | )
398 | .name('github');
399 |
400 | if (import.meta.env.DEV) {
401 | const debugControl = gui.addFolder('debug');
402 |
403 | // タイルの境界線を表示
404 | debugControl.add(map, 'showTileBoundaries').name('タイルの境界線を表示');
405 | }
406 |
--------------------------------------------------------------------------------
/src/protocol/debug.ts:
--------------------------------------------------------------------------------
1 | import { demEntry } from '../utils';
2 |
3 | export const DEBUG_Z = 11;
4 | export const DEBUG_X = 1813;
5 | export const DEBUG_Y = 808;
6 | export const HAS_DEBUG_TILE = false;
7 |
8 | export const debugTileImage = (tileUrl: string, buffer: ArrayBuffer) => {
9 | const debugTileId = demEntry.url.replace('{x}', DEBUG_X.toString()).replace('{y}', DEBUG_Y.toString()).replace('{z}', DEBUG_Z.toString());
10 | console.info(tileUrl, debugTileId);
11 | if (tileUrl !== debugTileId) {
12 | return;
13 | }
14 |
15 | console.info('Debug tile:', tileUrl);
16 | const debugCanvas = document.getElementById('debugCanvas') as HTMLCanvasElement;
17 | // const debugCanvas = document.createElement('canvas');
18 | debugCanvas.width = 256;
19 | debugCanvas.height = 256;
20 |
21 | const ctx = debugCanvas.getContext('2d') as CanvasRenderingContext2D;
22 | const blob = new Blob([buffer], { type: 'image/png' });
23 | const img = new Image();
24 |
25 | img.onload = () => {
26 | ctx.clearRect(0, 0, 256, 256);
27 | ctx.drawImage(img, 0, 0);
28 |
29 | // ダウンロード
30 | // const a = document.createElement('a');
31 | // a.href = debugCanvas.toDataURL();
32 | // a.download = `debug_${debugTileId}`;
33 | // a.click();
34 | };
35 |
36 | const url = URL.createObjectURL(blob);
37 | img.src = url;
38 | };
39 |
--------------------------------------------------------------------------------
/src/protocol/image.ts:
--------------------------------------------------------------------------------
1 | import colormap from 'colormap';
2 | import type { textureDataKey } from '../utils';
3 | import { textureData } from '../utils';
4 |
5 | type TileImageData = { [position: string]: { tileId: string; image: ImageBitmap } };
6 | // タイル画像
7 | export class TileImageManager {
8 | private static instance: TileImageManager;
9 | private cache: Map;
10 | private cacheSizeLimit: number;
11 | private cacheOrder: string[];
12 |
13 | private constructor(cacheSizeLimit = 500) {
14 | this.cache = new Map();
15 | this.cacheSizeLimit = cacheSizeLimit;
16 | this.cacheOrder = [];
17 | }
18 |
19 | // TileImageManager のインスタンスを取得する静的メソッド
20 | public static getInstance(cacheSizeLimit = 500): TileImageManager {
21 | if (!TileImageManager.instance) {
22 | TileImageManager.instance = new TileImageManager(cacheSizeLimit);
23 | }
24 | return TileImageManager.instance;
25 | }
26 |
27 | public async loadImage(src: string, signal: AbortSignal): Promise {
28 | try {
29 | const response = await fetch(src, { signal });
30 | if (!response.ok) {
31 | throw new Error('Failed to fetch image');
32 | }
33 | return await createImageBitmap(await response.blob());
34 | } catch (error) {
35 | if (error instanceof Error && error.name === 'AbortError') {
36 | // リクエストがキャンセルされた場合はエラーをスロー
37 | throw error;
38 | } else {
39 | // 他のエラー時には空の画像を返す
40 | return await createImageBitmap(new ImageData(1, 1));
41 | }
42 | }
43 | }
44 |
45 | public async getAdjacentTilesWithImages(x: number, y: number, z: number, baseurl: string, controller: AbortController): Promise {
46 | const positions = [
47 | { position: 'center', dx: 0, dy: 0 },
48 | { position: 'left', dx: -1, dy: 0 },
49 | { position: 'right', dx: 1, dy: 0 },
50 | { position: 'top', dx: 0, dy: -1 },
51 | { position: 'bottom', dx: 0, dy: 1 },
52 | ];
53 |
54 | const result: TileImageData = {};
55 |
56 | await Promise.all(
57 | positions.map(async ({ position, dx, dy }) => {
58 | const tileX = x + dx;
59 | const tileY = y + dy;
60 | const imageUrl = baseurl.replace('{x}', tileX.toString()).replace('{y}', tileY.toString()).replace('{z}', z.toString());
61 |
62 | // キャッシュにタイル画像があればそれを使う。なければ新たにリクエストを送る
63 | const imageBitmap = this.cache.has(imageUrl) ? this.cache.get(imageUrl) : await this.loadImage(imageUrl, controller.signal);
64 | if (!imageBitmap) return;
65 | this.add(imageUrl, imageBitmap);
66 |
67 | result[position] = { tileId: imageUrl, image: imageBitmap };
68 | }),
69 | );
70 |
71 | return result;
72 | }
73 |
74 | add(tileId: string, image: ImageBitmap): void {
75 | if (this.cacheOrder.length >= this.cacheSizeLimit) {
76 | const oldestTileId = this.cacheOrder.shift();
77 | if (oldestTileId) {
78 | this.cache.delete(oldestTileId);
79 | }
80 | }
81 |
82 | this.cache.set(tileId, image);
83 |
84 | const index = this.cacheOrder.indexOf(tileId);
85 | if (index > -1) {
86 | this.cacheOrder.splice(index, 1);
87 | }
88 | this.cacheOrder.push(tileId);
89 | }
90 |
91 | get(tileId: string): ImageBitmap | undefined {
92 | return this.cache.get(tileId);
93 | }
94 |
95 | has(tileId: string): boolean {
96 | return this.cache.has(tileId);
97 | }
98 |
99 | clear(): void {
100 | this.cache.clear();
101 | this.cacheOrder = [];
102 | }
103 | }
104 |
105 | // カラーマップ
106 | export class ColorMapManager {
107 | private cache: Map;
108 | public constructor() {
109 | this.cache = new Map();
110 | }
111 | public createColorArray(colorMapName: string, reverse: boolean = false): Uint8Array {
112 | // reverse フラグを含めてキャッシュキーを作成
113 | const cacheKey = `${colorMapName}_${reverse ? 'reversed' : 'normal'}`;
114 |
115 | if (this.has(cacheKey)) {
116 | return this.get(cacheKey) as Uint8Array;
117 | }
118 |
119 | const width = 256;
120 | const pixels = new Uint8Array(width * 3); // RGBのみの3チャンネルデータ
121 |
122 | // オプションオブジェクトを作成
123 | const options = {
124 | colormap: colorMapName,
125 | nshades: width,
126 | format: 'rgb', // RGBAからRGBに変更
127 | alpha: 1,
128 | };
129 |
130 | let colors = colormap(options as any);
131 |
132 | // reverse が true の場合、色の配列を反転
133 | if (reverse) {
134 | colors = colors.reverse();
135 | }
136 |
137 | // RGBデータの格納
138 | let ptr = 0;
139 | for (let i = 0; i < width; i++) {
140 | const color = colors[i] as number[];
141 | pixels[ptr++] = color[0];
142 | pixels[ptr++] = color[1];
143 | pixels[ptr++] = color[2];
144 | }
145 |
146 | // キャッシュに格納して再利用可能にする
147 | this.cache.set(cacheKey, pixels);
148 |
149 | return pixels;
150 | }
151 |
152 | add(cacheKey: string, pixels: Uint8Array): void {
153 | this.cache.set(cacheKey, pixels);
154 | }
155 |
156 | get(cacheKey: string): Uint8Array | undefined {
157 | return this.cache.get(cacheKey);
158 | }
159 |
160 | has(cacheKey: string): boolean {
161 | return this.cache.has(cacheKey);
162 | }
163 | }
164 |
165 | // テクスチャ
166 | export class TextureManager {
167 | private cache: Map;
168 |
169 | public constructor() {
170 | this.cache = new Map();
171 | }
172 |
173 | public async loadImage(key: textureDataKey): Promise {
174 | const path = textureData[key];
175 |
176 | if (this.has(key)) {
177 | return this.get(key) as ImageBitmap;
178 | }
179 |
180 | const imageData = await fetch(path)
181 | .then((response) => response.blob())
182 | .then((blob) => createImageBitmap(blob));
183 | this.cache.set(key, imageData);
184 |
185 | return imageData;
186 | }
187 |
188 | add(cacheKey: string, image: ImageBitmap): void {
189 | this.cache.set(cacheKey, image);
190 | }
191 |
192 | get(cacheKey: string): ImageBitmap | undefined {
193 | return this.cache.get(cacheKey);
194 | }
195 |
196 | has(cacheKey: string): boolean {
197 | return this.cache.has(cacheKey);
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/protocol/raster/index.ts:
--------------------------------------------------------------------------------
1 | import { DEM_DATA_TYPE, demEntry, tileOptions } from '../../utils';
2 | import type { DemDataTypeKey } from '../../utils';
3 | import { TileImageManager, ColorMapManager, TextureManager } from '../image';
4 | import { HAS_DEBUG_TILE, debugTileImage } from '../debug';
5 |
6 | class WorkerProtocol {
7 | private worker: Worker;
8 | private pendingRequests: Map<
9 | string,
10 | {
11 | resolve: (value: { data: Uint8Array } | PromiseLike<{ data: Uint8Array }>) => void;
12 | reject: (reason?: Error) => void;
13 | controller: AbortController;
14 | }
15 | >;
16 | private tileCache: TileImageManager;
17 | private colorMapCache: ColorMapManager;
18 | private textureCache: TextureManager;
19 |
20 | constructor(worker: Worker) {
21 | this.worker = worker;
22 | this.pendingRequests = new Map();
23 | this.tileCache = TileImageManager.getInstance(); // シングルトンインスタンスの取得
24 | this.colorMapCache = new ColorMapManager();
25 | this.textureCache = new TextureManager();
26 | this.worker.addEventListener('message', this.handleMessage);
27 | this.worker.addEventListener('error', this.handleError);
28 | }
29 |
30 | async request(url: URL, controller: AbortController): Promise<{ data: Uint8Array }> {
31 | // タイル座標からIDとURLを生成
32 | const x = parseInt(url.searchParams.get('x') || '0', 10);
33 | const y = parseInt(url.searchParams.get('y') || '0', 10);
34 | const z = parseInt(url.searchParams.get('z') || '0', 10);
35 | const demType = demEntry.demType as DemDataTypeKey;
36 | const maxzoom = demEntry.sourceMaxZoom;
37 | const baseUrl = demEntry.url;
38 |
39 | const onlyCenter = tileOptions.normalMapQuality.value === '中心タイルのみ';
40 |
41 | // 画像の取得
42 | const images = await this.tileCache.getAdjacentTilesWithImages(x, y, z, baseUrl, controller);
43 | const floodingImage = await this.textureCache.loadImage(demEntry.uniformsData.flooding.option.texture.value);
44 |
45 | return new Promise((resolve, reject) => {
46 | const center = images.center; // 中央のタイル
47 | const tileId = center.tileId; // ワーカー用ID
48 | const left = images.left; // 左のタイル
49 | const right = images.right; // 右のタイル
50 | const top = images.top; // 上のタイル
51 | const bottom = images.bottom; // 下のタイル
52 | this.pendingRequests.set(tileId, { resolve, reject, controller });
53 |
54 | const demTypeNumber = DEM_DATA_TYPE[demType as DemDataTypeKey];
55 |
56 | const elevationColorArray = this.colorMapCache.createColorArray(demEntry.uniformsData.elevation.option.colorMap.value, demEntry.uniformsData.elevation.option.colorMap.reverse);
57 | const slopeCorlorArray = this.colorMapCache.createColorArray(demEntry.uniformsData.slope.option.colorMap.value, demEntry.uniformsData.slope.option.colorMap.reverse);
58 | const aspectColorArray = this.colorMapCache.createColorArray(demEntry.uniformsData.aspect.option.colorMap.value, demEntry.uniformsData.aspect.option.colorMap.reverse);
59 |
60 | this.worker.postMessage({
61 | tileId,
62 | center: center.image,
63 | left: left.image,
64 | right: right.image,
65 | top: top.image,
66 | bottom: bottom.image,
67 | z,
68 | maxzoom,
69 | demTypeNumber,
70 | uniformsData: demEntry.uniformsData,
71 | elevationColorArray,
72 | slopeCorlorArray,
73 | aspectColorArray,
74 | floodingImage,
75 | onlyCenter,
76 | });
77 | });
78 | }
79 |
80 | private handleMessage = (e: MessageEvent) => {
81 | const { id, buffer, error } = e.data;
82 | const request = this.pendingRequests.get(id);
83 | if (error) {
84 | console.error(`Error processing tile ${id}:`, error);
85 | if (request) {
86 | request.reject(new Error(error));
87 | this.pendingRequests.delete(id);
88 | }
89 | } else if (request) {
90 | request.resolve({ data: buffer });
91 | this.pendingRequests.delete(id);
92 | }
93 |
94 | if (import.meta.env.MODE === 'development' && HAS_DEBUG_TILE) {
95 | debugTileImage(id, buffer);
96 | }
97 | };
98 |
99 | private handleError(e: ErrorEvent) {
100 | console.error('Worker error:', e);
101 | this.pendingRequests.forEach((request) => {
102 | request.reject(new Error('Worker error occurred'));
103 | });
104 | this.pendingRequests.clear();
105 | }
106 |
107 | // 全てのリクエストをキャンセル
108 | cancelAllRequests() {
109 | if (this.pendingRequests.size > 0) {
110 | this.pendingRequests.forEach(({ reject, controller }) => {
111 | controller.abort(); // AbortControllerをキャンセル
112 | reject(new Error('Request cancelled'));
113 | });
114 | }
115 |
116 | console.info('All requests have been cancelled.');
117 | this.pendingRequests.clear();
118 | }
119 |
120 | // タイルキャッシュをクリア
121 | clearCache() {
122 | this.tileCache.clear();
123 | }
124 | }
125 |
126 | class WorkerProtocolPool {
127 | private workers: WorkerProtocol[] = [];
128 | private workerIndex = 0;
129 | private poolSize: number;
130 |
131 | constructor(poolSize: number) {
132 | this.poolSize = poolSize;
133 |
134 | // 指定されたプールサイズのワーカープロトコルを作成
135 | for (let i = 0; i < poolSize; i++) {
136 | const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
137 | this.workers.push(new WorkerProtocol(worker));
138 | }
139 | }
140 |
141 | // ラウンドロビン方式で次のワーカーを取得
142 | private getNextWorker(): WorkerProtocol {
143 | const worker = this.workers[this.workerIndex];
144 | this.workerIndex = (this.workerIndex + 1) % this.poolSize;
145 | return worker;
146 | }
147 |
148 | // タイルリクエストを処理する
149 | async request(url: URL, controller: AbortController): Promise<{ data: Uint8Array }> {
150 | const worker = this.getNextWorker();
151 | return worker.request(url, controller);
152 | }
153 |
154 | // 全てのリクエストをキャンセル
155 | cancelAllRequests() {
156 | this.workers.forEach((worker) => worker.cancelAllRequests());
157 | }
158 |
159 | // 全てのタイルキャッシュをクリア
160 | clearCache() {
161 | this.workers.forEach((worker) => worker.clearCache());
162 | }
163 | }
164 |
165 | // const coreCount = navigator.hardwareConcurrency || 4;
166 | // const optimalThreads = Math.max(1, Math.floor(coreCount * 0.75));
167 | const workerProtocolPool = new WorkerProtocolPool(4); // 4つのワーカースレッドを持つプールを作成
168 |
169 | export const demProtocol = (protocolName: string) => {
170 | return {
171 | protocolName,
172 | request: (params: { url: string }, abortController: AbortController) => {
173 | const urlWithoutProtocol = params.url.replace(`${protocolName}://`, '');
174 | const url = new URL(urlWithoutProtocol);
175 | return workerProtocolPool.request(url, abortController);
176 | },
177 | cancelAllRequests: () => workerProtocolPool.cancelAllRequests(),
178 | clearCache: () => workerProtocolPool.clearCache(),
179 | };
180 | };
181 |
--------------------------------------------------------------------------------
/src/protocol/raster/shader/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | #ifdef GL_FRAGMENT_PRECISION_HIGH
3 | precision highp float;
4 | #else
5 | precision mediump float;
6 | #define GLSLIFY 1
7 | #endif
8 |
9 | uniform bool u_only_center; // 中心タイルのみのフラグ
10 |
11 | uniform sampler2D u_height_map_center;
12 | uniform sampler2D u_height_map_left;
13 | uniform sampler2D u_height_map_right;
14 | uniform sampler2D u_height_map_top;
15 | uniform sampler2D u_height_map_bottom;
16 |
17 | uniform float u_dem_type; // mapbox(0.0), gsi(1.0), terrarium(2.0)
18 | uniform float u_zoom_level;
19 | uniform float u_max_zoom;
20 |
21 | uniform bool u_slope_mode;
22 | uniform bool u_elevation_mode;
23 | uniform bool u_shadow_mode;
24 | uniform bool u_aspect_mode;
25 | uniform bool u_curvature_mode;
26 | uniform bool u_edge_mode;
27 | uniform bool u_contour_mode;
28 | uniform bool u_flooding_mode;
29 |
30 | uniform sampler2D u_elevationMap;
31 | uniform sampler2D u_slopeMap;
32 | uniform sampler2D u_aspectMap;
33 | uniform sampler2D u_floodingImage;
34 |
35 | uniform float u_elevation_alpha;
36 | uniform float u_slope_alpha;
37 | uniform float u_aspect_alpha;
38 | uniform float u_curvature_alpha;
39 | uniform float u_edge_alpha;
40 | uniform float u_shadow_strength;
41 | uniform float u_ambient;
42 | uniform float u_contour_alpha;
43 | uniform float u_flooding_alpha;
44 |
45 | uniform vec4 u_shadow_color;
46 | uniform vec4 u_highlight_color;
47 | uniform vec4 u_ridge_color;
48 | uniform vec4 u_valley_color;
49 | uniform vec4 u_edge_color;
50 | uniform vec4 u_contour_color;
51 |
52 | uniform float u_ridge_threshold;
53 | uniform float u_valley_threshold;
54 | uniform float u_edge_intensity;
55 | uniform float u_contour_count;
56 | uniform float u_water_level;
57 |
58 | uniform float u_max_height;
59 | uniform float u_min_height;
60 | uniform float u_contour_max_height;
61 | uniform vec3 u_light_direction;
62 | in vec2 v_tex_coord ;
63 | out vec4 fragColor;
64 |
65 |
66 |
67 |
68 |
69 | // 高さ変換関数
70 | float convertToHeight(vec4 color) {
71 | vec3 rgb = color.rgb * 255.0;
72 |
73 | if (u_dem_type == 0.0) { // mapbox (TerrainRGB)
74 |
75 | return -10000.0 + dot(rgb, vec3(256.0 * 256.0, 256.0, 1.0)) * 0.1;
76 |
77 | } else if (u_dem_type == 1.0) { // gsi (地理院標高タイル)
78 | // 地理院標高タイルの無効値チェック (R, G, B) = (128, 0, 0)
79 | if (rgb == vec3(128.0, 0.0, 0.0)) {
80 | return -9999.0;
81 | }
82 |
83 | float total = dot(rgb, vec3(65536.0, 256.0, 1.0));
84 | return mix(total, total - 16777216.0, step(8388608.0, total)) * 0.01;
85 |
86 | } else if (u_dem_type == 2.0) { // terrarium (TerrariumRGB)
87 |
88 | return (rgb.r * 256.0 + rgb.g + rgb.b / 256.0) - 32768.0;
89 | }
90 | }
91 |
92 | // カラーマップ取得関数
93 | vec4 getColorFromMap(sampler2D map, float value) {
94 | return vec4(texture(map, vec2(value, 0.5)).rgb, 1.0);
95 | }
96 |
97 |
98 | const mat3 conv_c = mat3(vec3(0,-1, 0),vec3(-1, 4,-1), vec3(0,-1, 0));
99 |
100 | float conv(mat3 a, mat3 b){
101 | return dot(a[0],b[0]) + dot(a[1],b[1]) + dot(a[2],b[2]);
102 | }
103 |
104 |
105 |
106 | struct TerrainData {
107 | vec3 normal;
108 | float curvature;
109 | };
110 | mat3 height_matrix;
111 |
112 | TerrainData calculateTerrainData(vec2 uv) {
113 |
114 | TerrainData data;
115 | // 9マスピクセルのインデックス番号
116 | // ----------------------------
117 | // | [0][0] | [0][1] | [0][2] |
118 | // ----------------------------
119 | // | [1][0] | [1][1] | [1][2] |
120 | // ----------------------------
121 | // | [2][0] | [2][1] | [2][2] |
122 | // ----------------------------
123 |
124 | // height_mapの隣接タイル
125 | // ----------------------------
126 | // | | top | |
127 | // ----------------------------
128 | // | left | center | right |
129 | // ----------------------------
130 | // | | bottom | |
131 | // ----------------------------
132 |
133 | vec2 pixel_size = vec2(1.0) / 256.0;
134 |
135 | // 高さマトリックスの作成
136 | if(u_only_center){
137 | // 中心のテクスチャのみでサンプリング
138 | // 左上
139 | height_matrix[0][0] = convertToHeight(texture(
140 | u_height_map_center,
141 | u_only_center ? clamp(uv + vec2(-pixel_size.x, -pixel_size.y), vec2(0.0), vec2(1.0) - pixel_size) :
142 | (uv.x <= pixel_size.x && uv.y <= pixel_size.y) ? uv + vec2(1.0 - pixel_size.x, 1.0 - pixel_size.y) :
143 | (uv.y <= pixel_size.y) ? uv + vec2(-pixel_size.x, 1.0 - pixel_size.y) :
144 | (uv.x <= pixel_size.x) ? uv + vec2(1.0 - pixel_size.x, -pixel_size.y) :
145 | uv + vec2(-pixel_size.x, -pixel_size.y)
146 | ));
147 |
148 | // 上
149 | height_matrix[0][1] = convertToHeight(texture(
150 | u_height_map_center,
151 | u_only_center ? clamp(uv + vec2(0.0, -pixel_size.y), vec2(0.0), vec2(1.0) - pixel_size) :
152 | (uv.y <= pixel_size.y) ? uv + vec2(0.0, 1.0 - pixel_size.y) :
153 | uv + vec2(0.0, -pixel_size.y)
154 | ));
155 |
156 | // 右上
157 | height_matrix[0][2] = convertToHeight(texture(
158 | u_height_map_center,
159 | u_only_center ? clamp(uv + vec2(pixel_size.x, -pixel_size.y), vec2(0.0), vec2(1.0) - pixel_size) :
160 | (uv.x >= 1.0 - pixel_size.x && uv.y <= pixel_size.y) ? uv + vec2(-1.0 + pixel_size.x, 1.0 - pixel_size.y) :
161 | (uv.y <= pixel_size.y) ? uv + vec2(pixel_size.x, 1.0 - pixel_size.y) :
162 | (uv.x >= 1.0 - pixel_size.x) ? uv + vec2(-1.0 + pixel_size.x, -pixel_size.y) :
163 | uv + vec2(pixel_size.x, -pixel_size.y)
164 | ));
165 |
166 | // 左
167 | height_matrix[1][0] = convertToHeight(texture(
168 | u_height_map_center,
169 | u_only_center ? clamp(uv + vec2(-pixel_size.x, 0.0), vec2(0.0), vec2(1.0) - pixel_size) :
170 | (uv.x <= pixel_size.x) ? uv + vec2(1.0 - pixel_size.x, 0.0) :
171 | uv + vec2(-pixel_size.x, 0.0)
172 | ));
173 |
174 | // 中央
175 | height_matrix[1][1] = convertToHeight(texture(u_height_map_center, uv));
176 |
177 | // 右
178 | height_matrix[1][2] = convertToHeight(texture(
179 | u_height_map_center,
180 | u_only_center ? clamp(uv + vec2(pixel_size.x, 0.0), vec2(0.0), vec2(1.0) - pixel_size) :
181 | (uv.x >= 1.0 - pixel_size.x) ? uv + vec2(-1.0 + pixel_size.x, 0.0) :
182 | uv + vec2(pixel_size.x, 0.0)
183 | ));
184 |
185 | // 左下
186 | height_matrix[2][0] = convertToHeight(texture(
187 | u_height_map_center,
188 | u_only_center ? clamp(uv + vec2(-pixel_size.x, pixel_size.y), vec2(0.0), vec2(1.0) - pixel_size) :
189 | (uv.x <= pixel_size.x && uv.y >= 1.0 - pixel_size.y) ? uv + vec2(1.0 - pixel_size.x, -1.0 + pixel_size.y) :
190 | (uv.y >= 1.0 - pixel_size.y) ? uv + vec2(-pixel_size.x, -1.0 + pixel_size.y) :
191 | (uv.x <= pixel_size.x) ? uv + vec2(1.0 - pixel_size.x, pixel_size.y) :
192 | uv + vec2(-pixel_size.x, pixel_size.y)
193 | ));
194 |
195 | // 下
196 | height_matrix[2][1] = convertToHeight(texture(
197 | u_height_map_center,
198 | u_only_center ? clamp(uv + vec2(0.0, pixel_size.y), vec2(0.0), vec2(1.0) - pixel_size) :
199 | (uv.y >= 1.0 - pixel_size.y) ? uv + vec2(0.0, -1.0 + pixel_size.y) :
200 | uv + vec2(0.0, pixel_size.y)
201 | ));
202 |
203 | // 右下
204 | height_matrix[2][2] = convertToHeight(texture(
205 | u_height_map_center,
206 | u_only_center ? clamp(uv + vec2(pixel_size.x, pixel_size.y), vec2(0.0), vec2(1.0) - pixel_size) :
207 | (uv.x >= 1.0 - pixel_size.x && uv.y >= 1.0 - pixel_size.y) ? uv + vec2(-1.0 + pixel_size.x, -1.0 + pixel_size.y) :
208 | (uv.y >= 1.0 - pixel_size.y) ? uv + vec2(pixel_size.x, -1.0 + pixel_size.y) :
209 | (uv.x >= 1.0 - pixel_size.x) ? uv + vec2(-1.0 + pixel_size.x, pixel_size.y) :
210 | uv + vec2(pixel_size.x, pixel_size.y)
211 | ));
212 |
213 | } else {
214 | // 端の場合は隣接テクスチャからサンプル
215 | // 左上
216 | height_matrix[0][0] = convertToHeight(
217 | (uv.x <= pixel_size.x && uv.y <= pixel_size.y) ? texture(u_height_map_left, uv + vec2(1.0 - pixel_size.x, 1.0 - pixel_size.y)) :
218 | (uv.y <= pixel_size.y) ? texture(u_height_map_top, uv + vec2(-pixel_size.x, 1.0 - pixel_size.y)) :
219 | (uv.x <= pixel_size.x) ? texture(u_height_map_left, uv + vec2(1.0 - pixel_size.x, -pixel_size.y)) :
220 | texture(u_height_map_center, uv + vec2(-pixel_size.x, -pixel_size.y))
221 | );
222 |
223 | // 上
224 | height_matrix[0][1] = convertToHeight(
225 | (uv.y <= pixel_size.y) ? texture(u_height_map_top, uv + vec2(0.0, 1.0 - pixel_size.y)) :
226 | texture(u_height_map_center, uv + vec2(0.0, -pixel_size.y))
227 | );
228 |
229 | // 右上
230 | height_matrix[0][2] = convertToHeight(
231 | (uv.x >= 1.0 - pixel_size.x && uv.y <= pixel_size.y) ? texture(u_height_map_right, uv + vec2(-1.0 + pixel_size.x, 1.0 - pixel_size.y)) :
232 | (uv.y <= pixel_size.y) ? texture(u_height_map_top, uv + vec2(pixel_size.x, 1.0 - pixel_size.y)) :
233 | (uv.x >= 1.0 - pixel_size.x) ? texture(u_height_map_right, uv + vec2(-1.0 + pixel_size.x, -pixel_size.y)) :
234 | texture(u_height_map_center, uv + vec2(pixel_size.x, -pixel_size.y))
235 | );
236 |
237 | // 左
238 | height_matrix[1][0] = convertToHeight(
239 | (uv.x <= pixel_size.x) ? texture(u_height_map_left, uv + vec2(1.0 - pixel_size.x, 0.0)) :
240 | texture(u_height_map_center, uv + vec2(-pixel_size.x, 0.0))
241 | );
242 |
243 | // 中央
244 | height_matrix[1][1] = convertToHeight(texture(u_height_map_center, uv));
245 |
246 | // 右
247 | height_matrix[1][2] = convertToHeight(
248 | (uv.x >= 1.0 - pixel_size.x) ? texture(u_height_map_right, uv + vec2(-1.0 + pixel_size.x, 0.0)) :
249 | texture(u_height_map_center, uv + vec2(pixel_size.x, 0.0))
250 | );
251 |
252 | // 左下
253 | height_matrix[2][0] = convertToHeight(
254 | (uv.x <= pixel_size.x && uv.y >= 1.0 - pixel_size.y) ? texture(u_height_map_left, uv + vec2(1.0 - pixel_size.x, -1.0 + pixel_size.y)) :
255 | (uv.y >= 1.0 - pixel_size.y) ? texture(u_height_map_bottom, uv + vec2(-pixel_size.x, -1.0 + pixel_size.y)) :
256 | (uv.x <= pixel_size.x) ? texture(u_height_map_left, uv + vec2(1.0 - pixel_size.x, pixel_size.y)) :
257 | texture(u_height_map_center, uv + vec2(-pixel_size.x, pixel_size.y))
258 | );
259 |
260 | // 下
261 | height_matrix[2][1] = convertToHeight(
262 | (uv.y >= 1.0 - pixel_size.y) ? texture(u_height_map_bottom, uv + vec2(0.0, -1.0 + pixel_size.y)) :
263 | texture(u_height_map_center, uv + vec2(0.0, pixel_size.y))
264 | );
265 |
266 | // 右下
267 | height_matrix[2][2] = convertToHeight(
268 | (uv.x >= 1.0 - pixel_size.x && uv.y >= 1.0 - pixel_size.y) ? texture(u_height_map_right, uv + vec2(-1.0 + pixel_size.x, -1.0 + pixel_size.y)) :
269 | (uv.y >= 1.0 - pixel_size.y) ? texture(u_height_map_bottom, uv + vec2(pixel_size.x, -1.0 + pixel_size.y)) :
270 | (uv.x >= 1.0 - pixel_size.x) ? texture(u_height_map_right, uv + vec2(-1.0 + pixel_size.x, pixel_size.y)) :
271 | texture(u_height_map_center, uv + vec2(pixel_size.x, pixel_size.y))
272 | );
273 | }
274 |
275 | // 法線の計算
276 | data.normal.x = (height_matrix[0][0] + height_matrix[0][1] + height_matrix[0][2]) -
277 | (height_matrix[2][0] + height_matrix[2][1] + height_matrix[2][2]);
278 | data.normal.y = (height_matrix[0][0] + height_matrix[1][0] + height_matrix[2][0]) -
279 | (height_matrix[0][2] + height_matrix[1][2] + height_matrix[2][2]);
280 | data.normal.z = 2.0 * pixel_size.x * 256.0; // スケーリング係数
281 | data.normal = normalize(data.normal);
282 |
283 | // 曲率の計算
284 | data.curvature = conv(conv_c, height_matrix);
285 |
286 | return data;
287 | }
288 |
289 | // 傾斜量を計算する関数
290 | float calculateSlope(vec3 normal) {
291 | // 法線ベクトルのZ成分から傾斜角を計算
292 | float slope = acos(normal.z);
293 | // ラジアンから度に変換
294 | return degrees(slope);
295 | }
296 |
297 | // 等高線を生成する関数
298 | float createContours(float height) {
299 | float pal_num = u_contour_count; // 等高線の数
300 | const float smooth_factor = 0.5; // 滑らかさの制御
301 |
302 | // スムーズな等高線の生成
303 | float n = height;
304 | float contour = n * (1.0 - smooth_factor) + clamp(floor(n * (pal_num - 0.001)) / (pal_num - 1.0), 0.0, 1.0) * smooth_factor;
305 |
306 | return contour;
307 | }
308 |
309 | void main() {
310 | vec2 uv = v_tex_coord ;
311 | vec4 color = texture(u_height_map_center, uv);
312 |
313 | if(color.a == 0.0){
314 | // テクスチャなし、または透明ピクセルの場合
315 | fragColor = vec4(0.0, 0.0, 0.0, 0.0);
316 | return;
317 | }
318 |
319 | if (!u_elevation_mode && !u_slope_mode && !u_shadow_mode && !u_aspect_mode && !u_curvature_mode && !u_edge_mode && !u_contour_mode && !u_flooding_mode) {
320 | fragColor = color;
321 | return;
322 | }
323 |
324 | vec4 final_color = vec4(0.0, 0.0,0.0,0.0);
325 | bool need_normal = (u_slope_mode || u_aspect_mode || u_shadow_mode || u_edge_mode);
326 | bool need_curvature = (u_curvature_mode);
327 |
328 | TerrainData terrain_data;
329 | if (need_normal || need_curvature) {
330 | terrain_data = calculateTerrainData(uv);
331 | }
332 |
333 |
334 | if (u_elevation_mode) {
335 | float height = convertToHeight(color);
336 |
337 | if(-9999.0 == height){
338 | // 無効地の場合
339 | fragColor = vec4(0.0, 0.0, 0.0, 0.0);
340 | return;
341 | }
342 |
343 | float normalized_height = clamp((height - u_min_height) / (u_max_height - u_min_height), 0.0, 1.0);
344 | vec4 terrain_color = getColorFromMap(u_elevationMap, normalized_height);
345 | final_color = mix(final_color, terrain_color, u_elevation_alpha);
346 | }
347 |
348 | if (need_normal) {
349 | vec3 normal = terrain_data.normal;
350 |
351 | if (u_slope_mode) {
352 | float slope = calculateSlope(normal);
353 | float normalized_slope = clamp(slope / 90.0, 0.0, 1.0);
354 | vec4 slope_color = getColorFromMap(u_slopeMap, normalized_slope);
355 | final_color = mix(final_color, slope_color, u_slope_alpha);
356 | // NOTE: 放線のデバッグ
357 | // vec3 normalizedColor = (normal + 1.0) * 0.5;
358 | // final_color = vec4(normalizedColor, 1.0);
359 | }
360 |
361 | if (u_aspect_mode) {
362 | float aspect = atan(normal.y, normal.x);
363 | float normalized_aspect = (aspect + 3.14159265359) / (2.0 * 3.14159265359);
364 | vec4 aspect_color = getColorFromMap(u_aspectMap, normalized_aspect);
365 | final_color = mix(final_color, aspect_color, u_aspect_alpha);
366 | }
367 |
368 | if (u_shadow_mode) {
369 | vec3 view_direction = normalize(vec3(0.0, 0.0, 1.0)); // 視線ベクトル
370 | float highlight_strength = 0.5; // ハイライトの強度
371 | // 拡散光の計算
372 | float diffuse = max(dot(normal, u_light_direction), 0.0);
373 |
374 | // 環境光と拡散光の合成
375 | float shadow_factor = u_ambient + (1.0 - u_ambient) * diffuse;
376 | float shadow_alpha = (1.0 - shadow_factor) * u_shadow_strength;
377 |
378 | // ハイライトの計算
379 | vec3 reflect_dir = reflect(-u_light_direction, normal); // 反射ベクトル
380 | float spec = pow(max(dot(view_direction, reflect_dir), 0.0), 16.0); // スペキュラ成分(光沢の鋭さ)
381 | vec3 final_highlight = highlight_strength * spec * u_highlight_color.rgb; // ハイライトの最終的な強度と色
382 |
383 | // ハイライトと影を重ねる
384 | final_color.rgb = mix(final_color.rgb, u_shadow_color.rgb, shadow_alpha); // 影の適用
385 | final_color.rgb += final_highlight; // ハイライトの適用
386 | final_color.a = final_color.a * (1.0 - shadow_alpha) + shadow_alpha;
387 | }
388 | }
389 |
390 | if (need_curvature) {
391 | float z = 10.0 * exp2(14.0 - u_zoom_level); // ズームレベルに基づくスケーリング係数
392 |
393 | if (color.a == 0.0) {
394 | fragColor = vec4(0.0, 0.0, 0.0, 0.0);
395 | return;
396 | }
397 |
398 | float curvature = terrain_data.curvature;
399 | float scaled_curvature = terrain_data.curvature / z;
400 | float normalized_curvature = clamp((scaled_curvature + 1.0) / 2.0, 0.0, 1.0);
401 |
402 | vec4 curvature_color = vec4(0.0); // デフォルトで透明
403 |
404 | // 山の稜線の処理
405 | if (normalized_curvature >= u_ridge_threshold) {
406 | float intensity = (normalized_curvature - u_ridge_threshold) / (1.0 - u_ridge_threshold);
407 | curvature_color = vec4(u_ridge_color.rgb, intensity * u_curvature_alpha);
408 | }
409 | // 谷の処理
410 | else if (normalized_curvature <= u_valley_threshold) {
411 | float intensity = (u_valley_threshold - normalized_curvature) / u_valley_threshold;
412 | curvature_color = vec4(u_valley_color.rgb, intensity * u_curvature_alpha);
413 | }
414 |
415 | // アルファブレンディング
416 | final_color.rgb = mix(final_color.rgb, curvature_color.rgb, curvature_color.a);
417 | final_color.a = max(final_color.a, curvature_color.a);
418 | }
419 |
420 |
421 | if(u_edge_mode) {
422 |
423 |
424 | vec2 e = vec2(1.5/256.0, 0);
425 | float edge_x = abs(height_matrix[1][2] - height_matrix[1][0]); // 左右の高さ差
426 | float edge_y = abs(height_matrix[2][1] - height_matrix[0][1]); // 上下の高さ差
427 |
428 | float z = 0.5 * exp2(u_zoom_level - 17.0);
429 | float edge_intensity = z;
430 |
431 | float edge_strength = (edge_x + edge_y) * edge_intensity * u_edge_intensity;
432 |
433 | // エッジの透明度を考慮したブレンディング
434 | vec4 edge = vec4(u_edge_color.rgb, clamp(edge_strength, 0.0, 0.8) * u_edge_alpha);
435 |
436 | // アルファブレンディング
437 | final_color.rgb = mix(final_color.rgb, edge.rgb, edge.a);
438 | final_color.a = max(final_color.a, edge.a);
439 | }
440 |
441 | if (u_contour_mode) {
442 | // 等高線の生成
443 | float height = convertToHeight(color);
444 | float normalized_height = clamp((height - 0.0) / (u_contour_max_height - 0.0), 0.0, 1.0);
445 | float contour_lines = createContours(normalized_height);
446 |
447 | vec2 texel_size = 1.0 / vec2(256.0, 256.0);
448 | float height_right = createContours(clamp(convertToHeight(texture(u_height_map_center, uv + vec2(texel_size.x, 0.0))) / u_contour_max_height, 0.0, 1.0));
449 | float height_up = createContours(clamp(convertToHeight(texture(u_height_map_center, uv + vec2(0.0, texel_size.y))) / u_contour_max_height, 0.0, 1.0));
450 |
451 | // 境界を計算
452 | float edge_threshold = 0.01; // 境界を検出するためのしきい値
453 | float edge = step(edge_threshold, abs(contour_lines - height_right)) + step(edge_threshold, abs(contour_lines - height_up));
454 |
455 | // 最終的な色の計算
456 | vec3 col = final_color.rgb;
457 | vec3 outline_color = u_contour_color.rgb; // アウトラインの色(黒)
458 |
459 | // アウトラインを追加し、ライン以外は透明にする
460 | if (edge > 0.0) {
461 | vec4 final_contour_color = vec4(outline_color, u_contour_alpha);
462 | final_color.a = max(final_color.a, final_contour_color.a);
463 | final_color.rgb = mix(final_color.rgb, final_contour_color.rgb, final_contour_color.a);
464 | }
465 |
466 | }
467 |
468 |
469 | if (u_flooding_mode) {
470 | float height = convertToHeight(color);
471 | vec4 flooding_color = vec4(0.0, 0.0, 1.0, u_flooding_alpha); // デフォルトの浸水色
472 |
473 | if (height < u_water_level) {
474 | // 浸水箇所のテクスチャから色を取得し、floodingAlpha を適用
475 | flooding_color = vec4(texture(u_floodingImage, uv).rgb, u_flooding_alpha);
476 |
477 | // アルファブレンドによる最終的な色の適用
478 | final_color.rgb = mix(final_color.rgb, flooding_color.rgb, flooding_color.a);
479 | final_color.a = mix(final_color.a, flooding_color.a, u_flooding_alpha); // アルファもブレンド
480 | }
481 | }
482 |
483 |
484 | fragColor = final_color;
485 |
486 | }
--------------------------------------------------------------------------------
/src/protocol/raster/shader/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | in vec4 a_position;
3 | out vec2 v_tex_coord;
4 |
5 | void main() {
6 | gl_Position = a_position;
7 | v_tex_coord = vec2(a_position.x * 0.5 + 0.5, a_position.y * -0.5 + 0.5); // Y軸を反転
8 | }
--------------------------------------------------------------------------------
/src/protocol/raster/worker.ts:
--------------------------------------------------------------------------------
1 | import fsSource from './shader/fragment.glsl?raw';
2 | import vsSource from './shader/vertex.glsl?raw';
3 | import type { DemEntry } from '../../utils';
4 | import chroma from 'chroma-js';
5 |
6 | let gl: WebGL2RenderingContext | null = null;
7 | let program: WebGLProgram | null = null;
8 | let positionBuffer: WebGLBuffer | null = null;
9 |
10 | const calculateLightDirection = (azimuth: number, altitude: number) => {
11 | // 方位角と高度をラジアンに変換
12 | const azimuthRad = (azimuth * Math.PI) / 180;
13 | const altitudeRad = (altitude * Math.PI) / 180;
14 |
15 | // 光の方向ベクトルを計算
16 | const x = Math.cos(altitudeRad) * Math.sin(azimuthRad);
17 | const y = Math.sin(altitudeRad);
18 | const z = -Math.cos(altitudeRad) * Math.cos(azimuthRad); // 北がZ軸の負の方向
19 |
20 | return [x, y, z];
21 | };
22 |
23 | const initWebGL = (canvas: OffscreenCanvas) => {
24 | gl = canvas.getContext('webgl2');
25 | if (!gl) {
26 | throw new Error('WebGL not supported');
27 | }
28 |
29 | const loadShader = (gl: WebGL2RenderingContext, type: number, source: string): WebGLShader | null => {
30 | const shader = gl.createShader(type);
31 | if (!shader) {
32 | console.error('Unable to create shader');
33 | return null;
34 | }
35 | gl.shaderSource(shader, source);
36 | gl.compileShader(shader);
37 |
38 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
39 | console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
40 | gl.deleteShader(shader);
41 | return null;
42 | }
43 | return shader;
44 | };
45 |
46 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
47 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
48 | if (!vertexShader || !fragmentShader) {
49 | throw new Error('Failed to load shaders');
50 | }
51 |
52 | program = gl.createProgram();
53 | if (!program) {
54 | throw new Error('Failed to create program');
55 | }
56 | gl.attachShader(program, vertexShader);
57 | gl.attachShader(program, fragmentShader);
58 | gl.linkProgram(program);
59 |
60 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
61 | console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
62 | throw new Error('Failed to link program');
63 | }
64 |
65 | gl.useProgram(program);
66 |
67 | positionBuffer = gl.createBuffer();
68 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
69 | const positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
70 | gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
71 | const positionLocation = gl.getAttribLocation(program, 'a_position');
72 | gl.enableVertexAttribArray(positionLocation);
73 | gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
74 | };
75 |
76 | const canvas = new OffscreenCanvas(256, 256);
77 |
78 | const bindTextures = (gl: WebGL2RenderingContext, program: WebGLProgram, textures: { [name: string]: { image: ImageBitmap | Uint8Array; type: 'height' | 'colormap' } }) => {
79 | let textureUnit = gl.TEXTURE0;
80 |
81 | Object.entries(textures).forEach(([uniformName, { image, type }]) => {
82 | // テクスチャをバインド
83 | const texture = gl.createTexture();
84 | gl.activeTexture(textureUnit); // 現在のテクスチャユニットをアクティブ
85 | gl.bindTexture(gl.TEXTURE_2D, texture);
86 |
87 | const location = gl.getUniformLocation(program, uniformName);
88 | gl.uniform1i(location, textureUnit - gl.TEXTURE0);
89 |
90 | if (type === 'height') {
91 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image as ImageBitmap);
92 | } else {
93 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 256, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, image as Uint8Array);
94 | }
95 |
96 | // ラッピングとフィルタリングの設定
97 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
98 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
99 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
100 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
101 |
102 | textureUnit += 1; // 次のテクスチャユニットへ
103 | });
104 | };
105 |
106 | type UniformValue = {
107 | type: '1f' | '1i' | '4fv' | '3fv'; // 型指定
108 | value: number | Float32Array | Int32Array | number[];
109 | };
110 |
111 | type Uniforms = {
112 | [name: string]: UniformValue;
113 | };
114 |
115 | const setUniforms = (gl: WebGL2RenderingContext, program: WebGLProgram, uniforms: Uniforms): void => {
116 | for (const [name, { type, value }] of Object.entries(uniforms)) {
117 | const location = gl.getUniformLocation(program, name);
118 | if (location !== null) {
119 | (gl as any)[`uniform${type}`](location, value);
120 | }
121 | }
122 | };
123 |
124 | self.onmessage = async (e) => {
125 | const { center, left, right, top, bottom, tileId, z, maxzoom, demTypeNumber, uniformsData, elevationColorArray, slopeCorlorArray, aspectColorArray, floodingImage, onlyCenter } = e.data;
126 | try {
127 | if (!gl) {
128 | initWebGL(canvas);
129 | }
130 |
131 | if (!gl || !program || !positionBuffer) {
132 | throw new Error('WebGL initialization failed');
133 | }
134 |
135 | const { elevation, slope, shadow, aspect, curvature, edge, contour, flooding } = uniformsData as DemEntry['uniformsData'];
136 |
137 | const lightDirection = calculateLightDirection(shadow.option.azimuth.value, shadow.option.altitude.value);
138 |
139 | const uniforms: Uniforms = {
140 | u_dem_type: { type: '1f', value: demTypeNumber },
141 | u_only_center: { type: '1i', value: onlyCenter ? 1 : 0 },
142 | u_zoom_level: { type: '1f', value: z },
143 | u_max_zoom: { type: '1f', value: maxzoom },
144 | u_elevation_mode: { type: '1i', value: elevation.option.visible.value ? 1 : 0 },
145 | u_slope_mode: { type: '1i', value: slope.option.visible.value ? 1 : 0 },
146 | u_shadow_mode: { type: '1i', value: shadow.option.visible.value ? 1 : 0 },
147 | u_aspect_mode: { type: '1i', value: aspect.option.visible.value ? 1 : 0 },
148 | u_curvature_mode: { type: '1i', value: curvature.option.visible.value ? 1 : 0 },
149 | u_edge_mode: { type: '1i', value: edge.option.visible.value ? 1 : 0 },
150 | u_contour_mode: { type: '1i', value: contour.option.visible.value ? 1 : 0 },
151 | u_flooding_mode: { type: '1i', value: flooding.option.visible.value ? 1 : 0 },
152 | u_elevation_alpha: { type: '1f', value: elevation.option.opacity.value },
153 | u_slope_alpha: { type: '1f', value: slope.option.opacity.value },
154 | u_shadow_strength: { type: '1f', value: shadow.option.opacity.value },
155 | u_aspect_alpha: { type: '1f', value: aspect.option.opacity.value },
156 | u_curvature_alpha: { type: '1f', value: curvature.option.opacity.value },
157 | u_edge_alpha: { type: '1f', value: edge.option.opacity.value },
158 | u_contour_alpha: { type: '1f', value: contour.option.opacity.value },
159 | u_flooding_alpha: { type: '1f', value: flooding.option.opacity.value },
160 | u_ridge_color: { type: '4fv', value: chroma(curvature.option.ridgeColor.value).gl() },
161 | u_valley_color: { type: '4fv', value: chroma(curvature.option.valleyColor.value).gl() },
162 | u_edge_color: { type: '4fv', value: chroma(edge.option.edgeColor.value).gl() },
163 | u_shadow_color: { type: '4fv', value: chroma(shadow.option.shadowColor.value).gl() },
164 | u_highlight_color: { type: '4fv', value: chroma(shadow.option.highlightColor.value).gl() },
165 | u_contour_color: { type: '4fv', value: chroma(contour.option.contourColor.value).gl() },
166 | u_ambient: { type: '1f', value: shadow.option.ambient.value },
167 | u_ridge_threshold: { type: '1f', value: curvature.option.ridgeThreshold.value },
168 | u_valley_threshold: { type: '1f', value: curvature.option.valleyThreshold.value },
169 | u_edge_intensity: { type: '1f', value: edge.option.edgeIntensity.value },
170 | u_max_height: { type: '1f', value: elevation.option.maxHeight.value },
171 | u_min_height: { type: '1f', value: elevation.option.minHeight.value },
172 | u_contour_max_height: { type: '1f', value: contour.option.maxHeight.value },
173 | u_light_direction: { type: '3fv', value: lightDirection },
174 | u_contour_count: { type: '1f', value: contour.option.contourCount.value },
175 | u_water_level: { type: '1f', value: flooding.option.waterLevel.value },
176 | };
177 |
178 | setUniforms(gl, program, uniforms);
179 |
180 | // テクスチャ
181 | bindTextures(gl, program, {
182 | u_height_map_center: { image: center, type: 'height' },
183 | u_height_map_left: { image: left, type: 'height' },
184 | u_height_map_right: { image: right, type: 'height' },
185 | u_height_map_top: { image: top, type: 'height' },
186 | u_height_map_bottom: { image: bottom, type: 'height' },
187 | ...(elevation.option.visible.value ? { u_elevationMap: { image: elevationColorArray, type: 'colormap' } } : {}),
188 | ...(slope.option.visible.value ? { u_slopeMap: { image: slopeCorlorArray, type: 'colormap' } } : {}),
189 | ...(aspect.option.visible.value ? { u_aspectMap: { image: aspectColorArray, type: 'colormap' } } : {}),
190 | ...(flooding.option.visible.value ? { u_floodingImage: { image: floodingImage, type: 'height' } } : {}),
191 | });
192 |
193 | gl.clear(gl.COLOR_BUFFER_BIT);
194 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
195 |
196 | const blob = await canvas.convertToBlob();
197 | if (!blob) {
198 | throw new Error('Failed to convert canvas to blob');
199 | }
200 |
201 | const buffer = await blob.arrayBuffer();
202 | self.postMessage({ id: tileId, buffer });
203 | } catch (error) {
204 | if (error instanceof Error) {
205 | self.postMessage({ id: tileId, error: error.message });
206 | }
207 | }
208 | };
209 |
--------------------------------------------------------------------------------
/src/protocol/terrain/index.ts:
--------------------------------------------------------------------------------
1 | import { DEM_DATA_TYPE, demEntry } from '../../utils';
2 | import type { DemDataTypeKey } from '../../utils';
3 | import { TileImageManager } from '../image';
4 |
5 | class WorkerProtocol {
6 | private worker: Worker;
7 | private pendingRequests: Map<
8 | string,
9 | {
10 | resolve: (value: { data: Uint8Array } | PromiseLike<{ data: Uint8Array }>) => void;
11 | reject: (reason?: Error) => void;
12 | controller: AbortController;
13 | }
14 | >;
15 | private tileCache: TileImageManager;
16 |
17 | constructor(worker: Worker) {
18 | this.worker = worker;
19 | this.pendingRequests = new Map();
20 | this.tileCache = TileImageManager.getInstance(); // シングルトンインスタンスの取得
21 | this.worker.addEventListener('message', this.handleMessage);
22 | this.worker.addEventListener('error', this.handleError);
23 | }
24 |
25 | async request(imageUrl: string, controller: AbortController): Promise<{ data: Uint8Array }> {
26 | try {
27 | const demType = demEntry.demType;
28 | let image;
29 | if (this.tileCache.has(imageUrl)) {
30 | image = this.tileCache.get(imageUrl);
31 | } else {
32 | image = await this.tileCache.loadImage(imageUrl, controller.signal);
33 | this.tileCache.add(imageUrl, image);
34 | }
35 |
36 | return new Promise((resolve, reject) => {
37 | const demTypeNumber = DEM_DATA_TYPE[demType as DemDataTypeKey];
38 | this.pendingRequests.set(imageUrl, { resolve, reject, controller });
39 | this.worker.postMessage({ image, demTypeNumber, id: imageUrl });
40 |
41 | controller.signal.addEventListener('abort', () => {
42 | this.pendingRequests.delete(imageUrl);
43 | reject(new Error('Request aborted'));
44 | });
45 | });
46 | } catch (error) {
47 | return Promise.reject(error);
48 | }
49 | }
50 |
51 | private handleMessage = (e: MessageEvent) => {
52 | const { id, buffer, error } = e.data;
53 | const request = this.pendingRequests.get(id);
54 | if (error) {
55 | console.error(`Error processing tile ${id}:`, error);
56 | if (request) {
57 | request.reject(new Error(error));
58 | this.pendingRequests.delete(id);
59 | }
60 | } else if (request) {
61 | request.resolve({ data: buffer });
62 | this.pendingRequests.delete(id);
63 | } else {
64 | console.warn(`No pending request found for tile ${id}`);
65 | }
66 | };
67 |
68 | private handleError = (e: ErrorEvent) => {
69 | console.error('Worker error:', e);
70 | this.pendingRequests.forEach((request) => {
71 | request.reject(new Error('Worker error occurred'));
72 | });
73 | this.pendingRequests.clear();
74 | };
75 |
76 | // タイルキャッシュをクリア
77 | clearCache() {
78 | this.tileCache.clear();
79 | }
80 |
81 | // 全てのリクエストをキャンセル;
82 | cancelAllRequests = () => {
83 | if (this.pendingRequests.size > 0) {
84 | this.pendingRequests.forEach(({ reject, controller }) => {
85 | controller.abort(); // AbortControllerをキャンセル
86 | reject(new Error('Request cancelled'));
87 | });
88 | }
89 | this.pendingRequests.clear();
90 | };
91 | }
92 |
93 | const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
94 | const workerProtocol = new WorkerProtocol(worker);
95 |
96 | export const terrainProtocol = (protocolName: string) => {
97 | return {
98 | protocolName,
99 | request: (params: { url: string }, abortController: AbortController) => {
100 | const imageUrl = params.url.replace(`${protocolName}://`, '');
101 | return workerProtocol.request(imageUrl, abortController);
102 | },
103 | cancelAllRequests: () => workerProtocol.cancelAllRequests(),
104 | clearCache: () => workerProtocol.clearCache(),
105 | };
106 | };
107 |
--------------------------------------------------------------------------------
/src/protocol/terrain/shader/fragment.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | #ifdef GL_FRAGMENT_PRECISION_HIGH
3 | precision highp float;
4 | #else
5 | precision mediump float;
6 | #endif
7 |
8 | uniform sampler2D u_height_map;
9 | uniform float u_dem_type; // mapbox(0.0), gsi(1.0), terrarium(2.0)
10 | in vec2 v_tex_coord;
11 | out vec4 fragColor;
12 |
13 | void main() {
14 | vec4 color = texture(u_height_map, v_tex_coord);
15 |
16 | // u_dem_type が 0.0 の場合は color を返し、他の値の場合は処理を続ける
17 | float demTypeStep = step(0.5, u_dem_type); // 0.0 の場合は 0.0、他の値の場合は 1.0
18 | vec4 defaultColor = color;
19 |
20 | vec3 rgb = color.rgb * 255.0;
21 |
22 | // terrainRGBにおける高度0の色
23 | vec4 zero_elevation_color = vec4(1.0, 134.0, 160.0, 255.0) / 255.0;
24 |
25 | // defaultColorが完全に透明なら zero_elevation_color に設定
26 | if (defaultColor.a == 0.0) {
27 | defaultColor = zero_elevation_color;
28 | }
29 |
30 | float height;
31 |
32 | // 高さの計算 (u_dem_typeによって異なる処理)
33 | if (u_dem_type == 1.0) { // gsi(地理院標高タイル)
34 | float rgb_value = dot(rgb, vec3(65536.0, 256.0, 1.0));
35 | height = mix(rgb_value, rgb_value - 16777216.0, step(8388608.0, rgb_value)) * 0.01;
36 | height = (height + 10000.0) * 10.0;
37 | } else if (u_dem_type == 2.0) { // Terrarium-RGB
38 | height = (rgb.r * 256.0 + rgb.g + rgb.b / 256.0) - 32768.0;
39 | height = (height + 10000.0) * 10.0; // Mapbox RGB に合わせたスケーリング
40 | }
41 |
42 | // 地理院標高タイルまたはTerrarium-RGBの無効値または完全に透明なピクセルの判定
43 | float is_valid = float(
44 | (u_dem_type == 1.0 && (rgb.r != 128.0 || rgb.g != 0.0 || rgb.b != 0.0) && color.a != 0.0) ||
45 | (u_dem_type == 2.0 && color.a != 0.0)
46 | );
47 |
48 | // 標高カラーの計算
49 | vec4 elevationColor = vec4(
50 | floor(height / 65536.0) / 255.0,
51 | floor(mod(height / 256.0, 256.0)) / 255.0,
52 | mod(height, 256.0) / 255.0,
53 | 1.0
54 | );
55 |
56 | // 無効値の場合は zero_elevation_color を使用
57 | vec4 finalColor = mix(zero_elevation_color, elevationColor, is_valid);
58 |
59 | // demTypeStep に応じてデフォルトの色か標高色を選択
60 | fragColor = mix(defaultColor, finalColor, demTypeStep);
61 | }
--------------------------------------------------------------------------------
/src/protocol/terrain/shader/vertex.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | in vec4 a_position;
3 | out vec2 v_tex_coord;
4 |
5 | void main() {
6 | gl_Position = a_position;
7 | v_tex_coord = vec2(a_position.x * 0.5 + 0.5, a_position.y * -0.5 + 0.5);
8 | }
--------------------------------------------------------------------------------
/src/protocol/terrain/worker.ts:
--------------------------------------------------------------------------------
1 | import fsSource from './shader/fragment.glsl?raw';
2 | import vsSource from './shader/vertex.glsl?raw';
3 |
4 | let gl: WebGL2RenderingContext | null = null;
5 | let program: WebGLProgram | null = null;
6 | let positionBuffer: WebGLBuffer | null = null;
7 | let heightMapLocation: WebGLUniformLocation | null = null;
8 | let demTypeLocation: WebGLUniformLocation | null = null;
9 |
10 | const initWebGL = (canvas: OffscreenCanvas) => {
11 | gl = canvas.getContext('webgl2');
12 | if (!gl) {
13 | throw new Error('WebGL not supported');
14 | }
15 |
16 | const loadShader = (gl: WebGL2RenderingContext, type: number, source: string): WebGLShader | null => {
17 | const shader = gl.createShader(type);
18 | if (!shader) {
19 | console.error('Unable to create shader');
20 | return null;
21 | }
22 | gl.shaderSource(shader, source);
23 | gl.compileShader(shader);
24 |
25 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
26 | console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
27 | gl.deleteShader(shader);
28 | return null;
29 | }
30 | return shader;
31 | };
32 |
33 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
34 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
35 | if (!vertexShader || !fragmentShader) {
36 | throw new Error('Failed to load shaders');
37 | }
38 |
39 | program = gl.createProgram();
40 | if (!program) {
41 | throw new Error('Failed to create program');
42 | }
43 | gl.attachShader(program, vertexShader);
44 | gl.attachShader(program, fragmentShader);
45 | gl.linkProgram(program);
46 |
47 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
48 | console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
49 | throw new Error('Failed to link program');
50 | }
51 |
52 | positionBuffer = gl.createBuffer();
53 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
54 | const positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
55 | gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
56 | const positionLocation = gl.getAttribLocation(program, 'a_position');
57 | gl.enableVertexAttribArray(positionLocation);
58 | gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
59 |
60 | heightMapLocation = gl.getUniformLocation(program, 'u_height_map');
61 | demTypeLocation = gl.getUniformLocation(program, 'u_dem_type');
62 | };
63 |
64 | const canvas = new OffscreenCanvas(256, 256);
65 |
66 | self.onmessage = async (e) => {
67 | const { id, image, demTypeNumber } = e.data;
68 |
69 | try {
70 | if (!gl) {
71 | initWebGL(canvas);
72 | }
73 |
74 | if (!gl || !program || !positionBuffer || !heightMapLocation) {
75 | throw new Error('WebGL initialization failed');
76 | }
77 |
78 | const heightMap = gl.createTexture();
79 | gl.activeTexture(gl.TEXTURE0);
80 | gl.bindTexture(gl.TEXTURE_2D, heightMap);
81 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
82 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
83 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
84 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
85 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
86 |
87 | gl.useProgram(program);
88 | gl.uniform1i(heightMapLocation, 0);
89 | gl.uniform1f(demTypeLocation, demTypeNumber); // demTypeを設定
90 |
91 | gl.clear(gl.COLOR_BUFFER_BIT);
92 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
93 |
94 | const blob = await canvas.convertToBlob();
95 | if (!blob) {
96 | throw new Error('Failed to convert canvas to blob');
97 | }
98 | const buffer = await blob.arrayBuffer();
99 | self.postMessage({ id: id, buffer });
100 | } catch (error) {
101 | if (error instanceof Error) {
102 | self.postMessage({ id: id, error: error.message });
103 | }
104 | }
105 | };
106 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | overflow: hidden;
5 | }
6 |
7 | #map {
8 | position: absolute;
9 | width: 100%;
10 | height: 100%;
11 | }
12 |
13 | #gui {
14 | position: absolute;
15 | top: 0;
16 | left: 0;
17 | z-index: 1000;
18 | margin: 10px;
19 | }
20 |
21 | .lil-gui {
22 | --name-width: 80px;
23 | --input-padding: 4px 8px;
24 | --slider-input-width: 4%;
25 | --color-input-width: 4%;
26 | max-height: calc(100vh - 50px);
27 | }
28 |
29 | #debugCanvas {
30 | position: absolute;
31 | top: 0;
32 | right: 0;
33 | z-index: 1000;
34 | margin: 10px;
35 | width: 256px;
36 | height: 256px;
37 | }
38 |
39 | @media (max-width: 768px) {
40 | #gui {
41 | margin: 0px;
42 | opacity: 0.9;
43 | }
44 |
45 | .lil-gui {
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { SourceSpecification } from 'maplibre-gl';
2 |
3 | export type DemData = {
4 | id: string;
5 | name: string;
6 | tiles: string[];
7 | tileSize: number;
8 | minzoom: number;
9 | maxzoom: number;
10 | bbox: [number, number, number, number];
11 | attribution: string;
12 | demType: DemDataTypeKey;
13 | };
14 |
15 | export const DEM_DATA_TYPE = {
16 | mapbox: 0.0,
17 | gsi: 1.0,
18 | terrarium: 2.0,
19 | } as const;
20 |
21 | export type DemDataType = typeof DEM_DATA_TYPE;
22 | export type DemDataTypeKey = keyof DemDataType;
23 |
24 | export const demLayers: DemData[] = [
25 | {
26 | id: 'dem_10b',
27 | name: '基盤地図情報数値標高モデル DEM10B',
28 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/dem_png/{z}/{x}/{y}.png'],
29 | tileSize: 256,
30 | minzoom: 1,
31 | maxzoom: 14,
32 | attribution: '国土地理院',
33 | bbox: [122.935, 20.425, 153.986, 45.551],
34 | demType: 'gsi',
35 | },
36 | {
37 | id: 'dem_5a',
38 | name: '基盤地図情報数値標高モデル DEM5A',
39 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/dem5a_png/{z}/{x}/{y}.png'],
40 | tileSize: 256,
41 | minzoom: 1,
42 | maxzoom: 15,
43 | attribution: '国土地理院',
44 | bbox: [122.935, 20.425, 153.986, 45.551],
45 | demType: 'gsi',
46 | },
47 | {
48 | id: 'dem_5b',
49 | name: '基盤地図情報数値標高モデル DEM5B',
50 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/dem5b_png/{z}/{x}/{y}.png'],
51 | tileSize: 256,
52 | minzoom: 1,
53 | maxzoom: 15,
54 | attribution: '国土地理院',
55 | bbox: [122.935, 20.425, 153.986, 45.551],
56 | demType: 'gsi',
57 | },
58 | {
59 | id: 'dem_5c',
60 | name: '基盤地図情報数値標高モデル DEM5C',
61 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/dem5c_png/{z}/{x}/{y}.png'],
62 | tileSize: 256,
63 | minzoom: 1,
64 | maxzoom: 15,
65 | attribution: '国土地理院',
66 | bbox: [122.935, 20.425, 153.986, 45.551],
67 | demType: 'gsi',
68 | },
69 | {
70 | id: 'tochigi_dem',
71 | name: '栃木県 数値標高モデル(DEM)0.5m',
72 | tiles: ['https://rinya-tochigi.geospatial.jp/2023/rinya/tile/terrainRGB/{z}/{x}/{y}.png'],
73 | tileSize: 256,
74 | minzoom: 2,
75 | maxzoom: 18,
76 | bbox: [139.326731, 36.199924, 140.291983, 37.155039],
77 | attribution: '栃木県',
78 | demType: 'mapbox',
79 | },
80 | {
81 | id: 'kochi_dem',
82 | name: '高知県 数値標高モデル(DEM)0.5m',
83 | tiles: ['https://rinya-kochi.geospatial.jp/2023/rinya/tile/terrainRGB/{z}/{x}/{y}.png'],
84 | tileSize: 256,
85 | minzoom: 2,
86 | maxzoom: 18,
87 | bbox: [132.479888, 32.702505, 134.31367, 33.882997],
88 | attribution: '高知県',
89 | demType: 'mapbox',
90 | },
91 | // {
92 | // id: 'hyougo_dem',
93 | // name: '兵庫県 数値標高モデル(DEM)0.5m',
94 | // tiles: ['https://rinya-hyogo.geospatial.jp/2023/rinya/tile/terrainRGB/{z}/{x}/{y}.png'],
95 | // tileSize: 256,
96 | // minzoom: 2,
97 | // maxzoom: 18,
98 | // bbox: [134.252809, 34.156129, 135.468591, 35.674667],
99 | // attribution: '兵庫県',
100 |
101 | // demType: 'mapbox',
102 | // },
103 | {
104 | id: 'hyougo_dem',
105 | name: '兵庫県 DEM 1m',
106 | tiles: ['https://tiles.gsj.jp/tiles/elev/hyogodem/{z}/{y}/{x}.png'],
107 | tileSize: 256,
108 | minzoom: 2,
109 | maxzoom: 17,
110 | bbox: [134.252809, 34.156129, 135.468591, 35.674667],
111 | attribution: '兵庫県',
112 | demType: 'gsi',
113 | },
114 | {
115 | id: 'hyougo_dsm',
116 | name: '兵庫県 DSM 1m',
117 | tiles: ['https://tiles.gsj.jp/tiles/elev/hyogodsm/{z}/{y}/{x}.png'],
118 | tileSize: 256,
119 | minzoom: 2,
120 | maxzoom: 17,
121 | bbox: [134.252809, 34.156129, 135.468591, 35.674667],
122 | attribution: '兵庫県',
123 | demType: 'gsi',
124 | },
125 | {
126 | id: 'tokyo',
127 | name: '東京都',
128 | tiles: ['https://tiles.gsj.jp/tiles/elev/tokyo/{z}/{y}/{x}.png'],
129 | tileSize: 256,
130 | minzoom: 2,
131 | maxzoom: 19,
132 | bbox: [138.942922, 32.440258, 139.861054, 35.898368],
133 | attribution: '産総研シームレス標高タイル',
134 | demType: 'gsi',
135 | },
136 | {
137 | id: 'kanagawa',
138 | name: '神奈川県',
139 | tiles: ['https://tiles.gsj.jp/tiles/elev/kanagawa/{z}/{y}/{x}.png'],
140 | tileSize: 256,
141 | minzoom: 2,
142 | maxzoom: 18,
143 | bbox: [138.9401382, 35.1277755, 139.7471238, 35.6730819],
144 | attribution: '産総研シームレス標高タイル',
145 | demType: 'gsi',
146 | },
147 | {
148 | id: 'rinya_toyama_dem',
149 | name: '富山県 DEM',
150 | tiles: ['https://forestgeo.info/opendata/16_toyama/dem_terrainRGB_2021/{z}/{x}/{y}.png'],
151 | tileSize: 256,
152 | minzoom: 2,
153 | maxzoom: 18,
154 | bbox: [136.7502827592869323, 36.2410658946756428, 137.8086007336188743, 37.0181547688286585],
155 | attribution: '林野庁',
156 | demType: 'mapbox',
157 | },
158 | {
159 | id: 'rinya_toyama_dchm',
160 | name: '富山県 dchm',
161 | tiles: ['https://forestgeo.info/opendata/16_toyama/dchm_terrainRGB_2021/{z}/{x}/{y}.png'],
162 | tileSize: 256,
163 | minzoom: 2,
164 | maxzoom: 18,
165 | bbox: [136.7502827592869323, 36.2410658946756428, 137.8086007336188743, 37.0181547688286585],
166 | attribution: '林野庁',
167 | demType: 'mapbox',
168 | },
169 | {
170 | id: 'rinya_nagaoka_dem',
171 | name: '林野庁 DEM 0.5m(長岡地域2024)',
172 | tiles: ['https://cf192141.cloudfree.jp/nagaoka/dem_terrainRGB_15_2024/{z}/{x}/{y}.png'],
173 | tileSize: 256,
174 | minzoom: 2,
175 | maxzoom: 18,
176 | bbox: [138.4714917, 37.145854, 139.2533006, 37.744387],
177 | attribution: '林野庁',
178 | demType: 'mapbox',
179 | },
180 | {
181 | id: 'rinya_nagaoka_dsm',
182 | name: '林野庁 DCHM 0.5m(長岡地域2024)',
183 | tiles: ['https://cf192141.cloudfree.jp/nagaoka/dchm_terrainRGB_15_2024/{z}/{x}/{y}.png'],
184 | tileSize: 256,
185 | minzoom: 2,
186 | maxzoom: 18,
187 | bbox: [138.4714917, 37.145854, 139.2533006, 37.744387],
188 | attribution: '林野庁',
189 | demType: 'mapbox',
190 | },
191 |
192 | {
193 | id: 'astergdemv3',
194 | name: 'ASTER全球3次元地形データ',
195 | tiles: ['https://tiles.gsj.jp/tiles/elev/astergdemv3/{z}/{y}/{x}.png'],
196 | tileSize: 256,
197 | minzoom: 1,
198 | maxzoom: 12,
199 | attribution: '産総研シームレス標高タイル',
200 | bbox: [-180, -85.051129, 180, 85.051129],
201 | demType: 'gsi',
202 | },
203 | {
204 | id: 'gebco',
205 | name: 'GEBCO Grid',
206 | tiles: ['https://tiles.gsj.jp/tiles/elev/gebco/{z}/{y}/{x}.png'],
207 | tileSize: 256,
208 | minzoom: 0,
209 | maxzoom: 9,
210 | attribution: '産総研シームレス標高タイル',
211 | bbox: [-180, -85.051129, 180, 85.051129],
212 | demType: 'gsi',
213 | },
214 | {
215 | id: 'tilezen',
216 | name: 'Tilezen Joerd',
217 | tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
218 | tileSize: 256,
219 | minzoom: 0,
220 | maxzoom: 15,
221 | attribution: 'Tilezen Joerd: Attribution',
222 | bbox: [-180, -85.051129, 180, 85.051129],
223 | demType: 'terrarium',
224 | },
225 | {
226 | id: 'lakedepth',
227 | name: '湖水深タイル',
228 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/lakedepth/{z}/{x}/{y}.png'],
229 | tileSize: 256,
230 | minzoom: 1,
231 | maxzoom: 14,
232 | attribution: '国土地理院',
233 | bbox: [122.935, 20.425, 153.986, 45.551],
234 | demType: 'gsi',
235 | },
236 | {
237 | id: 'gsigeoid',
238 | name: 'ジオイド・モデル「日本のジオイド2011」',
239 | tiles: ['https://tiles.gsj.jp/tiles/elev/gsigeoid/{z}/{y}/{x}.png'],
240 | tileSize: 256,
241 | minzoom: 0,
242 | maxzoom: 8,
243 | attribution: '産総研シームレス標高タイル',
244 | bbox: [120, 20, 150, 50],
245 | demType: 'gsi',
246 | },
247 | // {
248 | // id: 'mixed',
249 | // name: '統合DEM',
250 | // tiles: ['https://tiles.gsj.jp/tiles/elev/mixed/{z}/{y}/{x}.png'],
251 | // tileSize: 256,
252 | // minzoom: 0,
253 | // maxzoom: 15,
254 | // attribution: '産総研シームレス標高タイル',
255 | // bbox: [-180, -85.051129, 180, 85.051129],
256 | // demType: 'gsi',
257 | // },
258 | ];
259 |
260 | const COLOR_MAP_TYPE = [
261 | 'jet',
262 | 'hsv',
263 | 'hot',
264 | 'spring',
265 | 'summer',
266 | 'autumn',
267 | 'winter',
268 | 'bone',
269 | 'copper',
270 | 'greys',
271 | 'yignbu',
272 | 'greens',
273 | 'yiorrd',
274 | 'bluered',
275 | 'rdbu',
276 | 'picnic',
277 | 'rainbow',
278 | 'portland',
279 | 'blackbody',
280 | 'earth',
281 | 'electric',
282 | 'viridis',
283 | 'inferno',
284 | 'magma',
285 | 'plasma',
286 | 'warm',
287 | 'cool',
288 | 'rainbow-soft',
289 | 'bathymetry',
290 | 'cdom',
291 | 'chlorophyll',
292 | 'density',
293 | 'freesurface-blue',
294 | 'freesurface-red',
295 | 'oxygen',
296 | 'par',
297 | 'phase',
298 | 'salinity',
299 | 'temperature',
300 | 'turbidity',
301 | 'velocity-blue',
302 | 'velocity-green',
303 | 'cubehelix',
304 | ] as const;
305 |
306 | export type ColorMapType = (typeof COLOR_MAP_TYPE)[number];
307 | const mutableColorMapType: ColorMapType[] = [...COLOR_MAP_TYPE];
308 |
309 | type BooleanParameter = {
310 | name: string;
311 | value: boolean;
312 | };
313 |
314 | type NumberParameter = {
315 | name: string;
316 | value: number;
317 | min: number;
318 | max: number;
319 | step: number;
320 | };
321 |
322 | type ColorParameter = {
323 | name: string;
324 | value: string;
325 | };
326 |
327 | export type colorMapParameter = {
328 | name: string;
329 | value: ColorMapType;
330 | reverse: boolean;
331 | selection: ColorMapType[];
332 | };
333 |
334 | export const textureData = {
335 | water: 'water-bg-pattern-03.jpg',
336 | magma: 'magma-bg-pattern.jpg',
337 | } as const;
338 |
339 | export type textureDataKey = keyof typeof textureData;
340 |
341 | type TextureParameter = {
342 | name: string;
343 | value: textureDataKey; // valueはtextureDataのkeyのみを受け入れる
344 | selection: textureDataKey[];
345 | };
346 |
347 | export type DemEntry = {
348 | name: string;
349 | demType: DemDataTypeKey;
350 | url: string;
351 | attribution: string;
352 | sourceMinZoom: number;
353 | sourceMaxZoom: number;
354 | layerMinZoom?: number;
355 | layerMaxZoom?: number;
356 | bbox: [number, number, number, number];
357 | uniformsData: {
358 | elevation: {
359 | name: string;
360 | showMenu: boolean;
361 | option: {
362 | visible: BooleanParameter;
363 | opacity: NumberParameter;
364 | maxHeight: NumberParameter;
365 | minHeight: NumberParameter;
366 | colorMap: colorMapParameter;
367 | };
368 | };
369 | shadow: {
370 | name: string;
371 | showMenu: boolean;
372 | option: {
373 | visible: BooleanParameter;
374 | opacity: NumberParameter;
375 | shadowColor: ColorParameter;
376 | highlightColor: ColorParameter;
377 | ambient: NumberParameter;
378 | azimuth: NumberParameter;
379 | altitude: NumberParameter;
380 | };
381 | };
382 | aspect: {
383 | name: string;
384 | showMenu: boolean;
385 | option: {
386 | visible: BooleanParameter;
387 | opacity: NumberParameter;
388 | colorMap: colorMapParameter;
389 | };
390 | };
391 | slope: {
392 | name: string;
393 | showMenu: boolean;
394 | option: {
395 | visible: BooleanParameter;
396 | opacity: NumberParameter;
397 | colorMap: colorMapParameter;
398 | };
399 | };
400 | curvature: {
401 | name: string;
402 | showMenu: boolean;
403 | option: {
404 | visible: BooleanParameter;
405 | opacity: NumberParameter;
406 | ridgeThreshold: NumberParameter;
407 | ridgeColor: ColorParameter;
408 | valleyThreshold: NumberParameter;
409 | valleyColor: ColorParameter;
410 | };
411 | };
412 | edge: {
413 | name: string;
414 | showMenu: boolean;
415 | option: {
416 | visible: BooleanParameter;
417 | opacity: NumberParameter;
418 | edgeIntensity: NumberParameter;
419 | edgeColor: ColorParameter;
420 | };
421 | };
422 | contour: {
423 | name: string;
424 | showMenu: boolean;
425 | option: {
426 | visible: BooleanParameter;
427 | opacity: NumberParameter;
428 | contourCount: NumberParameter;
429 | maxHeight: NumberParameter;
430 | contourColor: ColorParameter;
431 | };
432 | };
433 | flooding: {
434 | name: string;
435 | showMenu: boolean;
436 | option: {
437 | visible: BooleanParameter;
438 | opacity: NumberParameter;
439 | waterLevel: NumberParameter;
440 | texture: TextureParameter;
441 | };
442 | };
443 | };
444 | };
445 |
446 | export const demEntry: DemEntry = {
447 | name: demLayers[0].name,
448 | demType: demLayers[0].demType,
449 | url: demLayers[0].tiles[0],
450 | sourceMaxZoom: demLayers[0].maxzoom,
451 | sourceMinZoom: demLayers[0].minzoom,
452 | attribution: demLayers[0].attribution,
453 | bbox: demLayers[0].bbox,
454 | uniformsData: {
455 | shadow: {
456 | name: '陰影',
457 | showMenu: true,
458 | option: {
459 | visible: {
460 | name: '表示',
461 | value: true,
462 | },
463 | opacity: {
464 | name: '不透明度',
465 | value: 0.7,
466 | min: 0,
467 | max: 1,
468 | step: 0.01,
469 | },
470 | shadowColor: {
471 | name: '陰影色',
472 | value: '#505050',
473 | },
474 | highlightColor: {
475 | name: 'ハイライト色',
476 | value: '#00ff9d',
477 | },
478 | ambient: {
479 | name: '環境光',
480 | value: 0.3,
481 | min: 0,
482 | max: 1,
483 | step: 0.01,
484 | },
485 | azimuth: {
486 | name: '方位',
487 | value: 0,
488 | min: 0,
489 | max: 360,
490 | step: 1,
491 | },
492 | altitude: {
493 | name: '高度',
494 | value: 30,
495 | min: 0,
496 | max: 90,
497 | step: 1,
498 | },
499 | },
500 | },
501 | edge: {
502 | name: 'エッジ',
503 | showMenu: true,
504 | option: {
505 | visible: {
506 | name: '表示',
507 | value: true,
508 | },
509 | opacity: {
510 | name: '不透明度',
511 | value: 0.9,
512 | min: 0,
513 | max: 1,
514 | step: 0.01,
515 | },
516 | edgeIntensity: {
517 | name: 'エッジ強度',
518 | value: 0.4,
519 | min: 0,
520 | max: 2,
521 | step: 0.01,
522 | },
523 | edgeColor: {
524 | name: 'エッジ色',
525 | value: '#ffffff',
526 | },
527 | },
528 | },
529 | elevation: {
530 | name: '標高',
531 | showMenu: true,
532 | option: {
533 | visible: {
534 | name: '表示',
535 | value: true,
536 | },
537 | opacity: {
538 | name: '不透明度',
539 | value: 0.8,
540 | min: 0,
541 | max: 1,
542 | step: 0.01,
543 | },
544 | maxHeight: {
545 | name: '最大標高',
546 | value: 4000,
547 | min: -10000,
548 | max: 10000,
549 | step: 0.1,
550 | },
551 | minHeight: {
552 | name: '最小標高',
553 | value: 0,
554 | min: -10000,
555 | max: 10000,
556 | step: 0.1,
557 | },
558 | colorMap: {
559 | name: 'カラーマップ',
560 | value: 'cool',
561 | reverse: false,
562 | selection: mutableColorMapType,
563 | },
564 | },
565 | },
566 | slope: {
567 | name: '傾斜量',
568 | showMenu: false,
569 | option: {
570 | visible: {
571 | name: '表示',
572 | value: false,
573 | },
574 | opacity: {
575 | name: '不透明度',
576 | value: 0.8,
577 | min: 0,
578 | max: 1,
579 | step: 0.01,
580 | },
581 | colorMap: {
582 | name: 'カラーマップ',
583 | value: 'summer',
584 | reverse: false,
585 | selection: mutableColorMapType,
586 | },
587 | },
588 | },
589 | aspect: {
590 | name: '傾斜方位',
591 | showMenu: false,
592 | option: {
593 | visible: {
594 | name: '表示',
595 | value: false,
596 | },
597 | opacity: {
598 | name: '不透明度',
599 | value: 0.8,
600 | min: 0,
601 | max: 1,
602 | step: 0.01,
603 | },
604 | colorMap: {
605 | name: 'カラーマップ',
606 | value: 'rainbow-soft',
607 | reverse: false,
608 | selection: mutableColorMapType,
609 | },
610 | },
611 | },
612 | curvature: {
613 | name: '曲率',
614 | showMenu: false,
615 | option: {
616 | visible: {
617 | name: '表示',
618 | value: false,
619 | },
620 | opacity: {
621 | name: '不透明度',
622 | value: 1.0,
623 | min: 0,
624 | max: 1,
625 | step: 0.01,
626 | },
627 | ridgeThreshold: {
628 | name: '尾根閾値',
629 | value: 0.5,
630 | min: 0,
631 | max: 1,
632 | step: 0.01,
633 | },
634 | ridgeColor: {
635 | name: '尾根色',
636 | value: '#ff1f1f',
637 | },
638 | valleyThreshold: {
639 | name: '谷閾値',
640 | value: 0.5,
641 | min: 0,
642 | max: 1,
643 | step: 0.01,
644 | },
645 | valleyColor: {
646 | name: '谷色',
647 | value: '#3c60ff',
648 | },
649 | },
650 | },
651 | contour: {
652 | name: '等高線',
653 | showMenu: false,
654 | option: {
655 | visible: {
656 | name: '表示',
657 | value: false,
658 | },
659 | opacity: {
660 | name: '不透明度',
661 | value: 0.7,
662 | min: 0,
663 | max: 1,
664 | step: 0.01,
665 | },
666 | contourCount: {
667 | name: '本数',
668 | value: 30,
669 | min: 1,
670 | max: 50,
671 | step: 1,
672 | },
673 | maxHeight: {
674 | name: '最大標高',
675 | value: 4000,
676 | min: 0,
677 | max: 10000,
678 | step: 0.1,
679 | },
680 | contourColor: {
681 | name: '色',
682 | value: '#000000',
683 | },
684 | },
685 | },
686 | flooding: {
687 | name: '浸水',
688 | showMenu: false,
689 | option: {
690 | visible: {
691 | name: '表示',
692 | value: false,
693 | },
694 | opacity: {
695 | name: '不透明度',
696 | value: 0.5,
697 | min: 0,
698 | max: 1,
699 | step: 0.01,
700 | },
701 | waterLevel: {
702 | name: '水位',
703 | value: 100,
704 | min: -10000,
705 | max: 10000,
706 | step: 0.1,
707 | },
708 | texture: {
709 | name: 'テクスチャ',
710 | value: 'water',
711 | selection: Object.keys(textureData) as (keyof typeof textureData)[],
712 | },
713 | },
714 | },
715 | },
716 | };
717 |
718 | export const tileOptions = {
719 | normalMapQuality: {
720 | name: '法線計算の精度',
721 | value: '隣接タイル込み',
722 | selection: ['隣接タイル込み', '中心タイルのみ'],
723 | },
724 | };
725 |
726 | export const backgroundSources: { [_: string]: SourceSpecification } = {
727 | 'MIERUNE mono': {
728 | type: 'raster',
729 | tiles: ['https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png'],
730 | tileSize: 256,
731 | minzoom: 0,
732 | maxzoom: 18,
733 | attribution:
734 | 'MIERUNE Inc. © OpenMapTiles © OpenStreetMap contributors',
735 | },
736 | '国土地理院 全国最新写真': {
737 | type: 'raster',
738 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg'],
739 | minzoom: 0,
740 | maxzoom: 19,
741 | tileSize: 256,
742 | attribution: '地理院タイル',
743 | },
744 | '国土地理院 淡色地図': {
745 | type: 'raster',
746 | tiles: ['https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'],
747 | minzoom: 0,
748 | maxzoom: 19,
749 | tileSize: 256,
750 | attribution: '地理院タイル',
751 | },
752 | 'OpenStreetMap': {
753 | type: 'raster',
754 | tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
755 | minzoom: 0,
756 | maxzoom: 19,
757 | tileSize: 256,
758 | attribution: '© OpenStreetMap contributors',
759 | },
760 | };
761 |
762 | type BBox = [number, number, number, number];
763 | export const isBBoxOverlapping = (bbox1: BBox, bbox2: BBox): boolean => {
764 | const [minX1, minY1, maxX1, maxY1] = bbox1;
765 | const [minX2, minY2, maxX2, maxY2] = bbox2;
766 | return minX1 < maxX2 && maxX1 > minX2 && minY1 < maxY2 && maxY1 > minY2;
767 | };
768 |
769 | export const isColorMapParameter = (param: any): param is colorMapParameter => {
770 | return typeof param === 'object' && typeof param.name === 'string' && typeof param.value === 'string' && typeof param.reverse === 'boolean' && Array.isArray(param.selection);
771 | };
772 |
773 | export const isTextureParameter = (param: any): param is TextureParameter => {
774 | return typeof param === 'object' && typeof param.name === 'string' && typeof param.value === 'string' && Array.isArray(param.selection);
775 | };
776 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true
21 | },
22 | "include": ["src"]
23 | }
24 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 |
3 | export default defineConfig({
4 | build: {
5 | outDir: 'dist',
6 | base: './',
7 | },
8 | base: './',
9 | });
10 |
--------------------------------------------------------------------------------