├── .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 | ![alt text](image.png) 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 | 2 | 🏔️ 3 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------