diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..7c6d950 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,34 @@ +name: Deploy + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + ref: + description: 'Branch or ref to build and deploy' + required: false + default: main + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.ref || github.ref }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + + - name: Build and deploy to gh-pages + env: + GIT_DEPLOY_USER: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + run: npm run deploy diff --git a/.gitignore b/.gitignore index d33e66d..3b462cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,23 @@ -/node_modules/ -/public/build/ -/public/style.json +node_modules +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS .DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index f3a6796..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -yarn pretty-quick --staged diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 1c71f60..0000000 --- a/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "arrowParens": "avoid", - "singleQuote": true -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..28d1e67 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f4a4c85..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-present, Stamen Design - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index f037a1a..0000000 --- a/babel.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: 'current', - }, - }, - ], - ], -}; diff --git a/example-styles/simple.json b/example-styles/simple.json new file mode 100644 index 0000000..180e1d0 --- /dev/null +++ b/example-styles/simple.json @@ -0,0 +1,148 @@ +{ + "version": 8, + "name": "simple", + "sources": { + "dummy": { "type": "vector", "tiles": [] } + }, + "layers": [ + { + "id": "land", + "type": "background", + "paint": { + "background-color": ["interpolate", ["linear"], ["zoom"], 0, "#fafafa", 10, "#cccccc", 22, "#333333"] + } + }, + { + "id": "fill-literal", + "type": "fill", + "source": "dummy", + "source-layer": "land", + "paint": { + "fill-color": "#6baed6" + } + }, + { + "id": "fill-linear", + "type": "fill", + "source": "dummy", + "source-layer": "land", + "paint": { + "fill-color": ["interpolate", ["linear"], ["zoom"], 0, "#f7fbff", 10, "#2171b5", 22, "#08306b"] + } + }, + { + "id": "fill-step", + "type": "fill", + "source": "dummy", + "source-layer": "land", + "paint": { + "fill-color": ["step", ["zoom"], "#ffffcc", 6, "#a1dab4", 12, "#41b6c4", 18, "#225ea8"] + } + }, + { + "id": "fill-match-class", + "type": "fill", + "source": "dummy", + "source-layer": "land", + "paint": { + "fill-color": ["match", ["get", "class"], "primary", "#e41a1c", "secondary", "#377eb8", "#999999"] + } + }, + { + "id": "fill-case-class", + "type": "fill", + "source": "dummy", + "source-layer": "land", + "paint": { + "fill-color": [ + "case", + ["==", ["get", "class"], "primary"], "#e41a1c", + ["==", ["get", "class"], "secondary"], "#377eb8", + "#999999" + ] + } + }, + { + "id": "line-literal", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": "#e34a33", + "line-width": 2 + } + }, + { + "id": "line-linear", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": ["interpolate", ["linear"], ["zoom"], 0, "#ffffb2", 10, "#fd8d3c", 22, "#bd0026"], + "line-width": ["interpolate", ["linear"], ["zoom"], 0, 1, 10, 4, 22, 16] + } + }, + { + "id": "line-step", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": ["step", ["zoom"], "#d9f0a3", 8, "#78c679", 14, "#238443"], + "line-width": ["step", ["zoom"], 1, 8, 4, 14, 10] + } + }, + { + "id": "line-match-class", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": ["match", ["get", "class"], "primary", "#e41a1c", "secondary", "#377eb8", "#999999"], + "line-width": ["match", ["get", "class"], "primary", 12, "secondary", 3, 1] + } + }, + { + "id": "line-case-class", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": [ + "case", + ["==", ["get", "class"], "primary"], "#e41a1c", + ["==", ["get", "class"], "secondary"], "#377eb8", + "#999999" + ], + "line-width": [ + "case", + ["==", ["get", "class"], "primary"], 12, + ["==", ["get", "class"], "secondary"], 3, + 1 + ] + } + }, + { + "id": "line-dashed", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": "#756bb1", + "line-width": 3, + "line-dasharray": [6, 3] + } + }, + { + "id": "line-dotted", + "type": "line", + "source": "dummy", + "source-layer": "road", + "paint": { + "line-color": "#e6550d", + "line-width": 4, + "line-dasharray": [0.5, 4] + } + } + ] +} diff --git a/example-styles/streets-v10.json b/example-styles/streets-v10.json new file mode 100644 index 0000000..6880d41 --- /dev/null +++ b/example-styles/streets-v10.json @@ -0,0 +1,4899 @@ +{ + "version": 8, + "name": "Mapbox Streets", + "metadata": { + "mapbox:type": "default", + "mapbox:origin": "streets-v11", + "mapbox:autocomposite": true, + "mapbox:groups": { + "1444855786460.0557": { "name": "Roads", "collapsed": true }, + "1444934295202.7542": { "name": "Admin boundaries", "collapsed": true }, + "1444855799204.86": { "name": "Bridges", "collapsed": true }, + "1444855769305.6016": { "name": "Tunnels", "collapsed": true } + } + }, + "sources": { + "composite": { + "url": "mapbox://mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2", + "type": "vector" + } + }, + "center": [0, 0], + "zoom": 3, + "sprite": "mapbox://sprites/mapbox/streets-v11", + "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "land", + "type": "background", + "metadata": {}, + "layout": {}, + "paint": { + "background-color": [ + "interpolate", + ["linear"], + ["zoom"], + 11, + "hsl(35, 32%, 91%)", + 13, + "hsl(35, 12%, 89%)" + ] + } + }, + { + "id": "landcover", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 7, + "layout": {}, + "paint": { + "fill-color": [ + "match", + ["get", "class"], + "snow", + "hsl(0, 0%, 100%)", + "hsl(75, 62%, 81%)" + ], + "fill-opacity": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 2, + 0.3, + 7, + 0 + ], + "fill-antialias": false + } + }, + { + "id": "national-park", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "landuse_overlay", + "minzoom": 5, + "filter": ["==", ["get", "class"], "national_park"], + "layout": {}, + "paint": { + "fill-color": "hsl(100, 58%, 76%)", + "fill-opacity": [ + "interpolate", + ["linear"], + ["zoom"], + 5, + 0, + 6, + 0.5, + 10, + 0.5 + ] + } + }, + { + "id": "landuse", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "landuse", + "minzoom": 5, + "filter": [ + "match", + ["get", "class"], + [ + "park", + "airport", + "cemetery", + "glacier", + "hospital", + "pitch", + "sand", + "school" + ], + true, + false + ], + "layout": {}, + "paint": { + "fill-color": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + [ + "match", + ["get", "class"], + "park", + "hsl(100, 58%, 76%)", + "airport", + "hsl(230, 15%, 86%)", + "cemetery", + "hsl(75, 37%, 81%)", + "glacier", + "hsl(196, 72%, 93%)", + "hospital", + "hsl(340, 37%, 87%)", + "pitch", + "hsl(100, 57%, 72%)", + "sand", + "hsl(60, 46%, 87%)", + "school", + "hsl(50, 47%, 81%)", + "hsla(0, 0%, 0%, 0)" + ], + 16, + [ + "match", + ["get", "class"], + "park", + "hsl(100, 58%, 76%)", + "airport", + "hsl(230, 29%, 89%)", + "cemetery", + "hsl(75, 37%, 81%)", + "glacier", + "hsl(196, 72%, 93%)", + "hospital", + "hsl(340, 63%, 89%)", + "pitch", + "hsl(100, 57%, 72%)", + "sand", + "hsl(60, 46%, 87%)", + "school", + "hsl(50, 63%, 84%)", + "hsla(0, 0%, 0%, 0)" + ] + ], + "fill-opacity": [ + "interpolate", + ["linear"], + ["zoom"], + 5, + 0, + 6, + ["match", ["get", "class"], "glacier", 0.5, 1] + ] + } + }, + { + "id": "pitch-outline", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "landuse", + "minzoom": 15, + "filter": ["==", ["get", "class"], "pitch"], + "layout": {}, + "paint": { "line-color": "hsl(75, 57%, 84%)" } + }, + { + "id": "water-shadow", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "water", + "layout": {}, + "paint": { + "fill-color": "hsl(215, 84%, 69%)", + "fill-translate": [ + "interpolate", + ["exponential", 1.2], + ["zoom"], + 7, + ["literal", [0, 0]], + 16, + ["literal", [-1, -1]] + ], + "fill-translate-anchor": "viewport" + } + }, + { + "id": "waterway", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "waterway", + "minzoom": 8, + "layout": { + "line-cap": ["step", ["zoom"], "butt", 11, "round"], + "line-join": "round" + }, + "paint": { + "line-color": "hsl(205, 87%, 76%)", + "line-width": [ + "interpolate", + ["exponential", 1.3], + ["zoom"], + 9, + ["match", ["get", "class"], ["canal", "river"], 0.1, 0], + 20, + ["match", ["get", "class"], ["canal", "river"], 8, 3] + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 8, 0, 8.5, 1] + } + }, + { + "id": "water", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "water", + "layout": {}, + "paint": { "fill-color": "hsl(196, 80%, 70%)" } + }, + { + "id": "hillshade", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "layout": {}, + "paint": { + "fill-color": [ + "match", + ["get", "class"], + "shadow", + "hsl(56, 59%, 22%)", + "hsl(0, 0%, 100%)" + ], + "fill-opacity": [ + "interpolate", + ["linear"], + ["zoom"], + 14, + ["match", ["get", "level"], [67, 56], 0.06, [89, 78], 0.05, 0.12], + 16, + 0 + ], + "fill-antialias": false + } + }, + { + "id": "land-structure-polygon", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "structure", + "minzoom": 13, + "filter": [ + "all", + ["==", ["geometry-type"], "Polygon"], + ["==", ["get", "class"], "land"] + ], + "layout": {}, + "paint": { "fill-color": "hsl(35, 12%, 89%)" } + }, + { + "id": "land-structure-line", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "structure", + "minzoom": 13, + "filter": [ + "all", + ["==", ["geometry-type"], "LineString"], + ["==", ["get", "class"], "land"] + ], + "layout": { "line-cap": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.99], + ["zoom"], + 14, + 0.75, + 20, + 40 + ], + "line-color": "hsl(35, 12%, 89%)" + } + }, + { + "id": "aeroway-polygon", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 11, + "filter": [ + "all", + ["==", ["geometry-type"], "Polygon"], + [ + "match", + ["get", "type"], + ["runway", "taxiway", "helipad"], + true, + false + ] + ], + "layout": {}, + "paint": { + "fill-color": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + "hsl(230, 23%, 82%)", + 16, + "hsl(230, 37%, 84%)" + ], + "fill-opacity": ["interpolate", ["linear"], ["zoom"], 11, 0, 11.5, 1] + } + }, + { + "id": "aeroway-line", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 9, + "filter": ["==", ["geometry-type"], "LineString"], + "layout": {}, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + "hsl(230, 23%, 82%)", + 16, + "hsl(230, 37%, 84%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 9, + ["match", ["get", "type"], "runway", 1, "taxiway", 0.5, 0.5], + 18, + ["match", ["get", "type"], "runway", 80, "taxiway", 20, 20] + ] + } + }, + { + "id": "building-outline", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ + "all", + ["!=", ["get", "type"], "building:part"], + ["==", ["get", "underground"], "false"] + ], + "layout": {}, + "paint": { + "line-color": "hsl(35, 6%, 79%)", + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 0.75, + 20, + 3 + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 15, 0, 16, 1] + } + }, + { + "id": "building", + "type": "fill", + "metadata": {}, + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ + "all", + ["!=", ["get", "type"], "building:part"], + ["==", ["get", "underground"], "false"] + ], + "layout": {}, + "paint": { + "fill-color": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + "hsl(35, 11%, 86%)", + 16, + "hsl(35, 8%, 85%)" + ], + "fill-opacity": ["interpolate", ["linear"], ["zoom"], 15, 0, 16, 1], + "fill-outline-color": "hsl(35, 6%, 79%)" + } + }, + { + "id": "tunnel-street-minor-low", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["street", "street_limited", "track", "primary_link"], + true, + false + ], + 14, + [ + "match", + ["get", "class"], + [ + "street", + "street_limited", + "track", + "primary_link", + "secondary_link", + "tertiary_link", + "service" + ], + true, + false + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 2, + "track", + 1, + 0.5 + ], + 18, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 18, + 12 + ] + ], + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": ["step", ["zoom"], 1, 14, 0] + } + }, + { + "id": "tunnel-street-minor-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["street", "street_limited", "track", "primary_link"], + true, + false + ], + 14, + [ + "match", + ["get", "class"], + [ + "street", + "street_limited", + "track", + "primary_link", + "secondary_link", + "tertiary_link", + "service" + ], + true, + false + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 2, + "track", + 1, + 0.5 + ], + 18, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 18, + 12 + ] + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1], + "line-dasharray": [3, 3] + } + }, + { + "id": "tunnel-primary-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + [ + "match", + ["get", "class"], + "primary", + 1, + ["secondary", "tertiary"], + 0.75, + 0.75 + ], + 18, + 2 + ], + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + [ + "match", + ["get", "class"], + "primary", + 0.75, + ["secondary", "tertiary"], + 0.1, + 0.1 + ], + 18, + [ + "match", + ["get", "class"], + "primary", + 32, + ["secondary", "tertiary"], + 26, + 26 + ] + ], + "line-dasharray": [3, 3] + } + }, + { + "id": "tunnel-major-link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-dasharray": [3, 3] + } + }, + { + "id": "tunnel-motorway-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + 1, + 18, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-dasharray": [3, 3] + } + }, + { + "id": "tunnel-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "construction"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": {}, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 2, + 18, + 18 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [0.4, 0.8]], + 15, + ["literal", [0.3, 0.6]], + 16, + ["literal", [0.2, 0.3]], + 17, + ["literal", [0.2, 0.25]], + 18, + ["literal", [0.15, 0.15]] + ] + } + }, + { + "id": "tunnel-path", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "path"], + ["!=", ["get", "type"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 1, + 18, + 4 + ], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [1, 0.5]] + ], + "line-color": "hsl(35, 26%, 95%)" + } + }, + { + "id": "tunnel-steps", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 1, + 16, + 1.6, + 18, + 6 + ], + "line-color": "hsl(35, 26%, 95%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [0.3, 0.3]] + ] + } + }, + { + "id": "tunnel-major-link", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsl(26, 100%, 78%)", + "trunk_link", + "hsl(46, 77%, 78%)", + "hsl(46, 77%, 78%)" + ] + } + }, + { + "id": "tunnel-pedestrian", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "pedestrian"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 18, + 12 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.5, 0.4]], + 16, + ["literal", [1, 0.2]] + ] + } + }, + { + "id": "tunnel-street-minor", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["street", "street_limited", "track", "primary_link"], + true, + false + ], + 14, + [ + "match", + ["get", "class"], + [ + "street", + "street_limited", + "track", + "primary_link", + "secondary_link", + "tertiary_link", + "service" + ], + true, + false + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 2, + "track", + 1, + 0.5 + ], + 18, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 18, + 12 + ] + ], + "line-color": [ + "match", + ["get", "class"], + "street_limited", + "hsl(35, 14%, 93%)", + "hsl(0, 0%, 100%)" + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "tunnel-primary-secondary-tertiary", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + [ + "match", + ["get", "class"], + "primary", + 0.75, + ["secondary", "tertiary"], + 0.1, + 0.1 + ], + 18, + [ + "match", + ["get", "class"], + "primary", + 32, + ["secondary", "tertiary"], + 26, + 26 + ] + ], + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "tunnel-oneway-arrow-blue", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "oneway"], "true"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "street", "street_limited", "tertiary"], + true, + false + ], + 16, + [ + "match", + ["get", "class"], + [ + "primary", + "secondary", + "tertiary", + "street", + "street_limited", + "primary_link", + "secondary_link", + "tertiary_link", + "path", + "pedestrian", + "service", + "track" + ], + true, + false + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": [ + "step", + ["zoom"], + "oneway-small", + 17, + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary", "street", "street_limited"], + "oneway-large", + "oneway-small" + ], + 18, + "oneway-large" + ], + "symbol-spacing": 200, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "tunnel-motorway-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway", + "hsl(26, 100%, 78%)", + "trunk", + "hsl(46, 77%, 78%)", + "hsl(46, 77%, 78%)" + ] + } + }, + { + "id": "tunnel-oneway-arrow-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + [ + "match", + ["get", "class"], + ["motorway", "motorway_link", "trunk", "trunk_link"], + true, + false + ], + ["==", ["get", "oneway"], "true"] + ], + "layout": { + "symbol-placement": "line", + "icon-image": [ + "step", + ["zoom"], + "oneway-white-small", + 17, + "oneway-white-large" + ], + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "ferry", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "minzoom": 8, + "filter": ["==", ["get", "type"], "ferry"], + "layout": { "line-join": "round" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + "hsl(205, 73%, 63%)", + 17, + "hsl(230, 73%, 63%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 20, + 1 + ], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 13, + ["literal", [12, 4]] + ] + } + }, + { + "id": "ferry-auto", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "filter": ["==", ["get", "type"], "ferry_auto"], + "layout": { "line-join": "round" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 15, + "hsl(205, 73%, 63%)", + 17, + "hsl(230, 73%, 63%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 20, + 1 + ] + } + }, + { + "id": "road-path-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "class"], "path"], + [ + "step", + ["zoom"], + [ + "!", + [ + "match", + ["get", "type"], + ["steps", "sidewalk", "crossing"], + true, + false + ] + ], + 16, + ["!=", ["get", "type"], "steps"] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 2, + 18, + 7 + ], + "line-color": "hsl(230, 17%, 82%)", + "line-opacity": ["step", ["zoom"], 0, 14, 0.75] + } + }, + { + "id": "road-steps-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "type"], "steps"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 2, + 17, + 4.6, + 18, + 7 + ], + "line-color": "hsl(230, 17%, 82%)", + "line-opacity": 0.75 + } + }, + { + "id": "turning-feature-outline", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["geometry-type"], "Point"], + [ + "match", + ["get", "class"], + ["turning_circle", "turning_loop"], + true, + false + ] + ], + "layout": { + "icon-image": "turning-circle-outline", + "icon-size": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.122, + 18, + 0.969, + 20, + 1 + ], + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "road-pedestrian-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "class"], "pedestrian"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 2, + 18, + 14.5 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "road-minor-low", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "step", + ["zoom"], + ["==", ["get", "class"], "track"], + 14, + [ + "match", + ["get", "class"], + ["track", "secondary_link", "tertiary_link", "service"], + true, + false + ] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + ["match", ["get", "class"], "track", 1, 0.5], + 18, + 12 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": ["step", ["zoom"], 1, 14, 0] + } + }, + { + "id": "road-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": ["step", ["zoom"], 1, 14, 0] + } + }, + { + "id": "road-minor-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "step", + ["zoom"], + ["==", ["get", "class"], "track"], + 14, + [ + "match", + ["get", "class"], + ["track", "secondary_link", "tertiary_link", "service"], + true, + false + ] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + ["match", ["get", "class"], "track", 1, 0.5], + 18, + 12 + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "road-street-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "road-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + 0.75, + 18, + 2 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.1, + 18, + 26 + ], + "line-opacity": ["step", ["zoom"], 0, 10, 1] + } + }, + { + "id": "road-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + ["==", ["get", "class"], "primary"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + 1, + 18, + 2 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-opacity": ["step", ["zoom"], 0, 10, 1] + } + }, + { + "id": "road-major-link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-opacity": ["step", ["zoom"], 0, 11, 1] + } + }, + { + "id": "road-motorway-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + 1, + 18, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-opacity": [ + "step", + ["zoom"], + ["match", ["get", "class"], "motorway", 1, 0], + 6, + 1 + ] + } + }, + { + "id": "road-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "class"], "construction"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": {}, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 2, + 18, + 18 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [0.4, 0.8]], + 15, + ["literal", [0.3, 0.6]], + 16, + ["literal", [0.2, 0.3]], + 17, + ["literal", [0.2, 0.25]], + 18, + ["literal", [0.15, 0.15]] + ] + } + }, + { + "id": "road-path", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "class"], "path"], + [ + "step", + ["zoom"], + [ + "!", + [ + "match", + ["get", "type"], + ["steps", "sidewalk", "crossing"], + true, + false + ] + ], + 16, + ["!=", ["get", "type"], "steps"] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 13, + 0.5, + 14, + 1, + 15, + 1, + 18, + 4 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [1, 0.5]] + ] + } + }, + { + "id": "road-steps", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "type"], "steps"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 1, + 16, + 1.6, + 18, + 6 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [0.3, 0.3]] + ] + } + }, + { + "id": "road-major-link", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsl(26, 100%, 68%)", + "trunk_link", + "hsl(46, 85%, 67%)", + "hsl(46, 85%, 67%)" + ] + } + }, + { + "id": "road-pedestrian", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "class"], "pedestrian"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 18, + 12 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.5, 0.4]], + 16, + ["literal", [1, 0.2]] + ] + } + }, + { + "id": "road-pedestrian-polygon-fill", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["geometry-type"], "Polygon"], + ["match", ["get", "class"], ["path", "pedestrian"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": {}, + "paint": { + "fill-color": [ + "interpolate", + ["linear"], + ["zoom"], + 16, + "hsl(230, 16%, 94%)", + 16.25, + "hsl(230, 50%, 98%)" + ], + "fill-outline-color": "hsl(230, 26%, 88%)" + } + }, + { + "id": "road-pedestrian-polygon-pattern", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["geometry-type"], "Polygon"], + ["match", ["get", "class"], ["path", "pedestrian"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-outline-color": "hsl(35, 10%, 83%)", + "fill-pattern": "pedestrian-polygon", + "fill-opacity": ["interpolate", ["linear"], ["zoom"], 16, 0, 16.25, 1] + } + }, + { + "id": "road-polygon", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["geometry-type"], "Polygon"], + [ + "match", + ["get", "class"], + [ + "primary", + "secondary", + "tertiary", + "primary_link", + "secondary_link", + "tertiary_link", + "street", + "street_limited", + "track", + "service" + ], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-outline-color": "#d6d9e6" + } + }, + { + "id": "road-minor", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "step", + ["zoom"], + ["==", ["get", "class"], "track"], + 14, + [ + "match", + ["get", "class"], + ["track", "secondary_link", "tertiary_link", "service"], + true, + false + ] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + ["match", ["get", "class"], "track", 1, 0.5], + 18, + 12 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "road-street", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-color": [ + "match", + ["get", "class"], + "street_limited", + "hsl(35, 14%, 93%)", + "hsl(0, 0%, 100%)" + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "road-secondary-tertiary", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.1, + 18, + 26 + ], + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "road-primary", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + ["==", ["get", "class"], "primary"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "road-oneway-arrow-blue", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "oneway"], "true"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary", "street", "street_limited"], + true, + false + ], + 16, + [ + "match", + ["get", "class"], + [ + "primary", + "secondary", + "tertiary", + "street", + "street_limited", + "primary_link", + "secondary_link", + "tertiary_link", + "path", + "pedestrian", + "service", + "track" + ], + true, + false + ] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": { + "symbol-placement": "line", + "icon-image": [ + "step", + ["zoom"], + "oneway-small", + 17, + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary", "street", "street_limited"], + "oneway-large", + "oneway-small" + ], + 18, + "oneway-large" + ], + "symbol-spacing": 200, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "road-motorway-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-color": [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + "motorway", + "hsl(26, 87%, 62%)", + "trunk", + "hsl(0, 0%, 100%)", + "hsl(0, 0%, 100%)" + ], + 6, + [ + "match", + ["get", "class"], + "motorway", + "hsl(26, 87%, 62%)", + "trunk", + "hsl(46, 80%, 60%)", + "hsl(46, 80%, 60%)" + ], + 9, + [ + "match", + ["get", "class"], + "motorway", + "hsl(26, 100%, 68%)", + "trunk", + "hsl(46, 85%, 67%)", + "hsl(46, 85%, 67%)" + ] + ] + } + }, + { + "id": "road-rail", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["major_rail", "minor_rail"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 13, + "hsl(50, 17%, 82%)", + 16, + "hsl(230, 10%, 74%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 20, + 1 + ] + } + }, + { + "id": "road-rail-tracks", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["major_rail", "minor_rail"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 13, + "hsl(50, 17%, 82%)", + 16, + "hsl(230, 10%, 74%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 4, + 20, + 8 + ], + "line-dasharray": [0.1, 15], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13.75, 0, 14, 1] + } + }, + { + "id": "level-crossing", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": ["==", ["get", "class"], "level_crossing"], + "layout": { "icon-image": "level-crossing", "icon-allow-overlap": true }, + "paint": {} + }, + { + "id": "road-oneway-arrow-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + ["==", ["get", "oneway"], "true"], + [ + "match", + ["get", "class"], + ["motorway", "trunk", "motorway_link", "trunk_link"], + true, + false + ], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ], + "layout": { + "symbol-placement": "line", + "icon-image": [ + "step", + ["zoom"], + "oneway-white-small", + 17, + "oneway-white-large" + ], + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "turning-feature", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["geometry-type"], "Point"], + [ + "match", + ["get", "class"], + ["turning_circle", "turning_loop"], + true, + false + ] + ], + "layout": { + "icon-image": "turning-circle", + "icon-size": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.095, + 18, + 1 + ], + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "golf-hole-line", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": ["==", ["get", "class"], "golf"], + "layout": {}, + "paint": { "line-color": "hsl(100, 47%, 58%)" } + }, + { + "id": "bridge-path-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "path"], + ["!=", ["get", "type"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 2, + 18, + 7 + ], + "line-color": "hsl(230, 17%, 82%)", + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "bridge-steps-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "type"], "steps"], + ["==", ["get", "structure"], "bridge"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 2, + 17, + 4.6, + 18, + 7 + ], + "line-color": "hsl(230, 17%, 82%)", + "line-opacity": 0.75 + } + }, + { + "id": "bridge-pedestrian-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "pedestrian"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 2, + 18, + 14.5 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "bridge-street-minor-low", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["street", "street_limited", "track", "primary_link"], + true, + false + ], + 14, + [ + "match", + ["get", "class"], + [ + "street", + "street_limited", + "track", + "primary_link", + "secondary_link", + "tertiary_link", + "service" + ], + true, + false + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 2, + "track", + 1, + 0.5 + ], + 18, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 18, + 12 + ] + ], + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": ["step", ["zoom"], 1, 14, 0] + } + }, + { + "id": "bridge-street-minor-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["street", "street_limited", "track", "primary_link"], + true, + false + ], + 14, + [ + "match", + ["get", "class"], + [ + "street", + "street_limited", + "track", + "primary_link", + "secondary_link", + "tertiary_link", + "service" + ], + true, + false + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 2, + "track", + 1, + 0.5 + ], + 18, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 18, + 12 + ] + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "bridge-primary-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + [ + "match", + ["get", "class"], + "primary", + 1, + ["secondary", "tertiary"], + 0.75, + 0.75 + ], + 18, + 2 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + [ + "match", + ["get", "class"], + "primary", + 0.75, + ["secondary", "tertiary"], + 0.1, + 0.1 + ], + 18, + [ + "match", + ["get", "class"], + "primary", + 32, + ["secondary", "tertiary"], + 26, + 26 + ] + ], + "line-opacity": ["step", ["zoom"], 0, 10, 1] + } + }, + { + "id": "bridge-major-link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ] + } + }, + { + "id": "bridge-motorway-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + 1, + 18, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ] + } + }, + { + "id": "bridge-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "construction"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": {}, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 2, + 18, + 18 + ], + "line-color": "hsl(230, 24%, 87%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [0.4, 0.8]], + 15, + ["literal", [0.3, 0.6]], + 16, + ["literal", [0.2, 0.3]], + 17, + ["literal", [0.2, 0.25]], + 18, + ["literal", [0.15, 0.15]] + ] + } + }, + { + "id": "bridge-path", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "path"], + ["!=", ["get", "type"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 1, + 18, + 4 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [1, 0.5]] + ] + } + }, + { + "id": "bridge-steps", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "type"], "steps"], + ["==", ["get", "structure"], "bridge"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 15, + 1, + 16, + 1.6, + 18, + 6 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [0.3, 0.3]] + ] + } + }, + { + "id": "bridge-major-link", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsl(26, 100%, 68%)", + "trunk_link", + "hsl(46, 85%, 67%)", + "hsl(46, 85%, 67%)" + ] + } + }, + { + "id": "bridge-pedestrian", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "pedestrian"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 18, + 12 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.5, 0.4]], + 16, + ["literal", [1, 0.2]] + ] + } + }, + { + "id": "bridge-street-minor", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["street", "street_limited", "track", "primary_link"], + true, + false + ], + 14, + [ + "match", + ["get", "class"], + [ + "street", + "street_limited", + "track", + "primary_link", + "secondary_link", + "tertiary_link", + "service" + ], + true, + false + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 2, + "track", + 1, + 0.5 + ], + 18, + [ + "match", + ["get", "class"], + ["street", "street_limited", "primary_link"], + 18, + 12 + ] + ], + "line-color": [ + "match", + ["get", "class"], + "street_limited", + "hsl(35, 14%, 93%)", + "hsl(0, 0%, 100%)" + ], + "line-opacity": ["step", ["zoom"], 0, 14, 1] + } + }, + { + "id": "bridge-primary-secondary-tertiary", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + [ + "match", + ["get", "class"], + "primary", + 0.75, + ["secondary", "tertiary"], + 0.1, + 0.1 + ], + 18, + [ + "match", + ["get", "class"], + "primary", + 32, + ["secondary", "tertiary"], + 26, + 26 + ] + ], + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "bridge-oneway-arrow-blue", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "oneway"], "true"], + [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary", "street", "street_limited"], + true, + false + ], + 16, + [ + "match", + ["get", "class"], + [ + "primary", + "secondary", + "tertiary", + "street", + "street_limited", + "primary_link", + "secondary_link", + "tertiary_link", + "path", + "pedestrian", + "track", + "service" + ], + true, + false + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": [ + "step", + ["zoom"], + "oneway-small", + 17, + [ + "match", + ["get", "class"], + ["primary", "secondary", "tertiary", "street", "street_limited"], + "oneway-large", + "oneway-small" + ], + 18, + "oneway-large" + ], + "symbol-spacing": 200, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "bridge-motorway-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway", + "hsl(26, 100%, 68%)", + "trunk", + "hsl(46, 85%, 67%)", + "hsl(46, 85%, 67%)" + ] + } + }, + { + "id": "bridge-rail", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["major_rail", "minor_rail"], true, false] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 13, + "hsl(50, 17%, 82%)", + 16, + "hsl(230, 10%, 74%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 20, + 1 + ] + } + }, + { + "id": "bridge-rail-tracks", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["major_rail", "minor_rail"], true, false] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 13, + "hsl(50, 17%, 82%)", + 16, + "hsl(230, 10%, 74%)" + ], + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 4, + 20, + 8 + ], + "line-dasharray": [0.1, 15], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13.75, 0, 14, 1] + } + }, + { + "id": "bridge-major-link-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.75, + 20, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ] + } + }, + { + "id": "bridge-motorway-trunk-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 10, + 1, + 18, + 2 + ], + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ] + } + }, + { + "id": "bridge-major-link-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + [ + "match", + ["get", "class"], + ["motorway_link", "trunk_link"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 12, + 0.5, + 14, + 2, + 18, + 18 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsl(26, 100%, 68%)", + "trunk_link", + "hsl(46, 85%, 67%)", + "hsl(46, 85%, 67%)" + ] + } + }, + { + "id": "bridge-motorway-trunk-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 5, + 0.75, + 18, + 32 + ], + "line-color": [ + "match", + ["get", "class"], + "motorway", + "hsl(26, 100%, 68%)", + "trunk", + "hsl(46, 85%, 67%)", + "hsl(46, 85%, 67%)" + ] + } + }, + { + "id": "bridge-oneway-arrow-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [ + "match", + ["get", "class"], + ["motorway", "trunk", "motorway_link", "trunk_link"], + true, + false + ], + ["==", ["get", "oneway"], "true"] + ], + "layout": { + "symbol-placement": "line", + "icon-image": [ + "step", + ["zoom"], + "oneway-white-small", + 17, + "oneway-white-large" + ], + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "aerialway", + "type": "line", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": ["==", ["get", "class"], "aerialway"], + "layout": { "line-join": "round" }, + "paint": { + "line-color": "hsl(230, 10%, 74%)", + "line-width": [ + "interpolate", + ["exponential", 1.5], + ["zoom"], + 14, + 0.5, + 20, + 1 + ] + } + }, + { + "id": "admin-1-boundary-bg", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "filter": [ + "all", + ["==", ["get", "admin_level"], 1], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": { "line-join": "bevel" }, + "paint": { + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 8, + "hsl(35, 12%, 89%)", + 16, + "hsl(230, 49%, 90%)" + ], + "line-width": ["interpolate", ["linear"], ["zoom"], 7, 3.75, 12, 5.5], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 7, 0, 8, 0.75], + "line-dasharray": [1, 0], + "line-translate": [0, 0], + "line-blur": ["interpolate", ["linear"], ["zoom"], 3, 0, 8, 3] + } + }, + { + "id": "admin-0-boundary-bg", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ + "all", + ["==", ["get", "admin_level"], 0], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": {}, + "paint": { + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 3.5, 10, 8], + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 6, + "hsl(35, 12%, 89%)", + 8, + "hsl(230, 49%, 90%)" + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 3, 0, 4, 0.5], + "line-translate": [0, 0], + "line-blur": ["interpolate", ["linear"], ["zoom"], 3, 0, 10, 2] + } + }, + { + "id": "admin-1-boundary", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "filter": [ + "all", + ["==", ["get", "admin_level"], 1], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [2, 0]], + 7, + ["literal", [2, 2, 6, 2]] + ], + "line-width": ["interpolate", ["linear"], ["zoom"], 7, 0.75, 12, 1.5], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 2, 0, 3, 1], + "line-color": [ + "interpolate", + ["linear"], + ["zoom"], + 3, + "hsl(230, 14%, 77%)", + 7, + "hsl(230, 8%, 62%)" + ] + } + }, + { + "id": "admin-0-boundary", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ + "all", + ["==", ["get", "admin_level"], 0], + ["==", ["get", "disputed"], "false"], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-color": "hsl(230, 8%, 51%)", + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.5, 10, 2] + } + }, + { + "id": "admin-0-boundary-disputed", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ + "all", + ["==", ["get", "disputed"], "true"], + ["==", ["get", "admin_level"], 0], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-dasharray": [1.5, 1.5], + "line-color": "hsl(230, 8%, 51%)", + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.5, 10, 2] + } + }, + { + "id": "building-number-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "housenum_label", + "minzoom": 17, + "layout": { + "text-field": ["get", "house_num"], + "text-font": ["DIN Offc Pro Italic", "Arial Unicode MS Regular"], + "text-padding": 4, + "text-max-width": 7, + "text-size": 9.5 + }, + "paint": { + "text-color": "hsl(35, 2%, 69%)", + "text-halo-color": "hsl(35, 8%, 85%)", + "text-halo-width": 0.5 + } + }, + { + "id": "road-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + ["motorway", "trunk", "primary", "secondary", "tertiary"], + true, + false + ], + 12, + [ + "match", + ["get", "class"], + [ + "motorway", + "trunk", + "primary", + "secondary", + "tertiary", + "pedestrian", + "street", + "street_limited" + ], + true, + false + ], + 15, + ["match", ["get", "class"], "golf", false, true] + ], + "layout": { + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 10, + [ + "match", + ["get", "class"], + ["motorway", "trunk", "primary", "secondary", "tertiary"], + 10, + [ + "motorway_link", + "trunk_link", + "primary_link", + "secondary_link", + "tertiary_link", + "pedestrian", + "street", + "street_limited" + ], + 9, + 6.5 + ], + 18, + [ + "match", + ["get", "class"], + ["motorway", "trunk", "primary", "secondary", "tertiary"], + 16, + [ + "motorway_link", + "trunk_link", + "primary_link", + "secondary_link", + "tertiary_link", + "pedestrian", + "street", + "street_limited" + ], + 14, + 13 + ] + ], + "text-max-angle": 30, + "text-font": ["DIN Offc Pro Regular", "Arial Unicode MS Regular"], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": [ + "match", + ["get", "class"], + "ferry", + "hsl(230, 48%, 44%)", + "hsl(0, 0%, 0%)" + ], + "text-halo-color": [ + "match", + ["get", "class"], + ["motorway", "trunk"], + "hsla(0, 0%, 100%, 0.75)", + "ferry", + "hsl(196, 80%, 70%)", + "hsl(0, 0%, 100%)" + ], + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "road-number-shield", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "minzoom": 6, + "filter": [ + "all", + ["has", "reflen"], + ["<=", ["get", "reflen"], 6], + [ + "step", + ["zoom"], + ["==", ["geometry-type"], "Point"], + 11, + [">", ["get", "len"], 5000], + 12, + [">", ["get", "len"], 2500], + 13, + [">", ["get", "len"], 1000], + 14, + true + ] + ], + "layout": { + "text-size": 9, + "icon-image": [ + "concat", + ["get", "shield"], + "-", + ["to-string", ["get", "reflen"]] + ], + "icon-rotation-alignment": "viewport", + "text-max-angle": 38, + "symbol-spacing": [ + "interpolate", + ["linear"], + ["zoom"], + 11, + 150, + 14, + 200 + ], + "text-font": ["DIN Offc Pro Bold", "Arial Unicode MS Bold"], + "symbol-placement": ["step", ["zoom"], "point", 11, "line"], + "text-rotation-alignment": "viewport", + "text-field": ["get", "ref"], + "text-letter-spacing": 0.05 + }, + "paint": { + "text-color": [ + "match", + ["get", "shield_text_color"], + "white", + "hsl(0, 0%, 100%)", + "black", + "hsl(0, 0%, 7%)", + "yellow", + "hsl(50, 100%, 70%)", + "orange", + "hsl(25, 100%, 75%)", + "blue", + "hsl(230, 48%, 34%)", + "hsl(0, 0%, 100%)" + ] + } + }, + { + "id": "road-exit-shield", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "motorway_junction", + "minzoom": 14, + "filter": ["all", ["has", "reflen"], ["<=", ["get", "reflen"], 9]], + "layout": { + "text-field": ["get", "ref"], + "text-size": 9, + "icon-image": [ + "concat", + "motorway-exit-", + ["to-string", ["get", "reflen"]] + ], + "text-font": ["DIN Offc Pro Bold", "Arial Unicode MS Bold"] + }, + "paint": { "text-color": "hsl(0, 0%, 100%)", "text-translate": [0, 0] } + }, + { + "id": "golf-hole-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": ["==", ["get", "class"], "golf"], + "layout": { + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "text-size": 12 + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5, + "text-color": "hsl(100, 100%, 20%)" + } + }, + { + "id": "waterway-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["canal", "river", "stream"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "text-font": ["DIN Offc Pro Italic", "Arial Unicode MS Regular"], + "text-max-angle": 30, + "symbol-spacing": [ + "interpolate", + ["linear", 1], + ["zoom"], + 15, + 250, + 17, + 400 + ], + "text-size": ["interpolate", ["linear"], ["zoom"], 13, 12, 18, 16], + "symbol-placement": "line", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { "text-color": "hsl(230, 48%, 44%)" } + }, + { + "id": "natural-line-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 4, + "filter": [ + "all", + ["match", ["get", "class"], ["glacier", "landform"], true, false], + ["==", ["geometry-type"], "LineString"], + ["<=", ["get", "filterrank"], 2] + ], + "layout": { + "text-size": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 18, 5, 12], + 17, + ["step", ["get", "sizerank"], 18, 13, 12] + ], + "text-max-angle": 30, + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "symbol-placement": "line-center", + "text-pitch-alignment": "viewport" + }, + "paint": { + "text-halo-width": 0.5, + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-blur": 0.5, + "text-color": [ + "step", + ["zoom"], + [ + "step", + ["get", "sizerank"], + "hsl(26, 20%, 42%)", + 5, + "hsl(26, 25%, 32%)" + ], + 17, + [ + "step", + ["get", "sizerank"], + "hsl(26, 20%, 42%)", + 13, + "hsl(26, 25%, 32%)" + ] + ] + } + }, + { + "id": "natural-point-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 4, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["dock", "glacier", "landform", "water_feature", "wetland"], + true, + false + ], + ["==", ["geometry-type"], "Point"], + ["<=", ["get", "filterrank"], 2] + ], + "layout": { + "text-size": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 18, 5, 12], + 17, + ["step", ["get", "sizerank"], 18, 13, 12] + ], + "icon-image": [ + "step", + ["zoom"], + ["concat", ["get", "maki"], "-11"], + 15, + ["concat", ["get", "maki"], "-15"] + ], + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "text-offset": [ + "step", + ["zoom"], + [ + "step", + ["get", "sizerank"], + ["literal", [0, 0]], + 5, + ["literal", [0, 0.75]] + ], + 17, + [ + "step", + ["get", "sizerank"], + ["literal", [0, 0]], + 13, + ["literal", [0, 0.75]] + ] + ], + "text-anchor": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], "center", 5, "top"], + 17, + ["step", ["get", "sizerank"], "center", 13, "top"] + ], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "icon-opacity": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 0, 5, 1], + 17, + ["step", ["get", "sizerank"], 0, 13, 1] + ], + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5, + "text-color": [ + "step", + ["zoom"], + [ + "step", + ["get", "sizerank"], + "hsl(26, 20%, 42%)", + 5, + "hsl(26, 25%, 32%)" + ], + 17, + [ + "step", + ["get", "sizerank"], + "hsl(26, 20%, 42%)", + 13, + "hsl(26, 25%, 32%)" + ] + ] + } + }, + { + "id": "water-line-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "natural_label", + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["bay", "ocean", "reservoir", "sea", "water"], + true, + false + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 7, + ["step", ["get", "sizerank"], 24, 6, 18, 12, 12], + 10, + ["step", ["get", "sizerank"], 18, 9, 12], + 18, + ["step", ["get", "sizerank"], 18, 9, 16] + ], + "text-max-angle": 30, + "text-letter-spacing": [ + "match", + ["get", "class"], + "ocean", + 0.25, + ["sea", "bay"], + 0.15, + 0 + ], + "text-font": ["DIN Offc Pro Italic", "Arial Unicode MS Regular"], + "symbol-placement": "line-center", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "text-color": [ + "match", + ["get", "class"], + ["bay", "ocean", "sea"], + "hsl(205, 84%, 88%)", + "hsl(230, 48%, 44%)" + ] + } + }, + { + "id": "water-point-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "natural_label", + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["bay", "ocean", "reservoir", "sea", "water"], + true, + false + ], + ["==", ["geometry-type"], "Point"] + ], + "layout": { + "text-line-height": 1.3, + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 7, + ["step", ["get", "sizerank"], 24, 6, 18, 12, 12], + 10, + ["step", ["get", "sizerank"], 18, 9, 12] + ], + "text-font": ["DIN Offc Pro Italic", "Arial Unicode MS Regular"], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": [ + "match", + ["get", "class"], + "ocean", + 0.25, + ["bay", "sea"], + 0.15, + 0.01 + ], + "text-max-width": [ + "match", + ["get", "class"], + "ocean", + 4, + "sea", + 5, + ["bay", "water"], + 7, + 10 + ] + }, + "paint": { + "text-color": [ + "match", + ["get", "class"], + ["bay", "ocean", "sea"], + "hsl(205, 84%, 88%)", + "hsl(230, 48%, 44%)" + ] + } + }, + { + "id": "poi-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 6, + "filter": [ + "<=", + ["get", "filterrank"], + ["+", ["step", ["zoom"], 0, 16, 1, 17, 2], 3] + ], + "layout": { + "text-size": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 18, 5, 12], + 17, + ["step", ["get", "sizerank"], 18, 13, 12] + ], + "icon-image": [ + "step", + ["zoom"], + ["concat", ["get", "maki"], "-11"], + 15, + ["concat", ["get", "maki"], "-15"] + ], + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "text-offset": [ + "step", + ["zoom"], + [ + "step", + ["get", "sizerank"], + ["literal", [0, 0]], + 5, + ["literal", [0, 0.75]] + ], + 17, + [ + "step", + ["get", "sizerank"], + ["literal", [0, 0]], + 13, + ["literal", [0, 0.75]] + ] + ], + "text-anchor": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], "center", 5, "top"], + 17, + ["step", ["get", "sizerank"], "center", 13, "top"] + ], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "icon-opacity": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 0, 5, 1], + 17, + ["step", ["get", "sizerank"], 0, 13, 1] + ], + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5, + "text-color": [ + "step", + ["zoom"], + [ + "step", + ["get", "sizerank"], + [ + "match", + ["get", "class"], + "food_and_drink", + "hsl(22, 55%, 55%)", + "park_like", + "hsl(100, 45%, 37%)", + "education", + "hsl(51, 40%, 40%)", + "medical", + "hsl(340, 30%, 52%)", + "hsl(26, 20%, 42%)" + ], + 5, + [ + "match", + ["get", "class"], + "food_and_drink", + "hsl(22, 85%, 38%)", + "park_like", + "hsl(100, 100%, 20%)", + "education", + "hsl(51, 100%, 20%)", + "medical", + "hsl(340, 39%, 42%)", + "hsl(26, 25%, 32%)" + ] + ], + 17, + [ + "step", + ["get", "sizerank"], + [ + "match", + ["get", "class"], + "food_and_drink", + "hsl(22, 55%, 55%)", + "park_like", + "hsl(100, 45%, 37%)", + "education", + "hsl(51, 40%, 40%)", + "medical", + "hsl(340, 30%, 52%)", + "hsl(26, 20%, 42%)" + ], + 13, + [ + "match", + ["get", "class"], + "food_and_drink", + "hsl(22, 85%, 38%)", + "park_like", + "hsl(100, 100%, 20%)", + "education", + "hsl(51, 100%, 20%)", + "medical", + "hsl(340, 39%, 42%)", + "hsl(26, 25%, 32%)" + ] + ] + ] + } + }, + { + "id": "transit-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "transit_stop_label", + "minzoom": 12, + "filter": [ + "step", + ["zoom"], + [ + "all", + ["match", ["get", "mode"], ["rail", "metro_rail"], true, false], + ["!=", ["get", "stop_type"], "entrance"] + ], + 15, + [ + "all", + [ + "match", + ["get", "mode"], + ["rail", "metro_rail", "ferry", "light_rail"], + true, + false + ], + ["!=", ["get", "stop_type"], "entrance"] + ], + 16, + [ + "all", + ["!=", ["get", "mode"], "bus"], + ["!=", ["get", "stop_type"], "entrance"] + ], + 17, + ["!=", ["get", "stop_type"], "entrance"], + 19, + true + ], + "layout": { + "text-size": 12, + "icon-image": ["get", "network"], + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "text-justify": [ + "match", + ["get", "stop_type"], + "entrance", + "left", + "center" + ], + "text-offset": [ + "match", + ["get", "stop_type"], + "entrance", + ["literal", [1, 0]], + ["literal", [0, 0.8]] + ], + "text-anchor": [ + "match", + ["get", "stop_type"], + "entrance", + "left", + "top" + ], + "text-field": [ + "step", + ["zoom"], + "", + 14, + [ + "match", + ["get", "mode"], + ["rail", "metro_rail"], + ["coalesce", ["get", "name_en"], ["get", "name"]], + "" + ], + 16, + [ + "match", + ["get", "mode"], + ["bus", "bicycle"], + "", + ["coalesce", ["get", "name_en"], ["get", "name"]] + ], + 18, + ["coalesce", ["get", "name_en"], ["get", "name"]] + ], + "text-letter-spacing": 0.01, + "text-max-width": ["match", ["get", "stop_type"], "entrance", 15, 9] + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-color": [ + "match", + ["get", "network"], + "entrance", + "hsl(230, 48%, 44%)", + "tokyo-metro", + "hsl(180, 50%, 30%)", + "mexico-city-metro", + "hsl(25, 100%, 40%)", + [ + "barcelona-metro", + "delhi-metro", + "hong-kong-mtr", + "milan-metro", + "osaka-subway" + ], + "hsl(0, 90%, 47%)", + ["boston-t", "washington-metro"], + "hsl(0, 0%, 0%)", + [ + "chongqing-rail-transit", + "kiev-metro", + "singapore-mrt", + "taipei-metro" + ], + "hsl(140, 90%, 25%)", + "hsl(230, 48%, 44%)" + ], + "text-halo-blur": 0.5, + "text-halo-width": 0.5 + } + }, + { + "id": "airport-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "airport_label", + "minzoom": 8, + "layout": { + "text-line-height": 1.1, + "text-size": ["step", ["get", "sizerank"], 18, 9, 12], + "icon-image": [ + "step", + ["get", "sizerank"], + ["concat", ["get", "maki"], "-15"], + 9, + ["concat", ["get", "maki"], "-11"] + ], + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "text-offset": [0, 0.75], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": [ + "step", + ["get", "sizerank"], + ["coalesce", ["get", "name_en"], ["get", "name"]], + 15, + ["get", "ref"] + ], + "text-letter-spacing": 0.01, + "text-max-width": 9 + }, + "paint": { + "text-color": "hsl(230, 48%, 44%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "settlement-subdivision-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 15, + "filter": [ + "all", + ["==", ["get", "class"], "settlement_subdivision"], + ["<=", ["get", "filterrank"], 4] + ], + "layout": { + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-transform": "uppercase", + "text-font": ["DIN Offc Pro Regular", "Arial Unicode MS Regular"], + "text-letter-spacing": [ + "match", + ["get", "type"], + "suburb", + 0.15, + ["quarter", "neighborhood"], + 0.1, + 0.1 + ], + "text-max-width": 7, + "text-padding": 3, + "text-size": [ + "interpolate", + ["cubic-bezier", 0.5, 0, 1, 1], + ["zoom"], + 11, + [ + "match", + ["get", "type"], + "suburb", + 11, + ["quarter", "neighborhood"], + 10.5, + 10.5 + ], + 15, + [ + "match", + ["get", "type"], + "suburb", + 17, + ["quarter", "neighborhood"], + 16, + 16 + ] + ] + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-color": "hsl(230, 29%, 35%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "settlement-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 15, + "filter": [ + "all", + ["<=", ["get", "filterrank"], 3], + ["==", ["get", "class"], "settlement"], + [ + "step", + ["zoom"], + true, + 13, + [">=", ["get", "symbolrank"], 11], + 14, + [">=", ["get", "symbolrank"], 13] + ] + ], + "layout": { + "icon-image": [ + "case", + ["==", ["get", "capital"], 2], + "border-dot-13", + ["step", ["get", "symbolrank"], "dot-11", 9, "dot-10", 11, "dot-9"] + ], + "text-font": [ + "step", + ["zoom"], + ["literal", ["DIN Offc Pro Regular", "Arial Unicode MS Regular"]], + 8, + [ + "step", + ["get", "symbolrank"], + ["literal", ["DIN Offc Pro Medium", "Arial Unicode MS Regular"]], + 11, + ["literal", ["DIN Offc Pro Regular", "Arial Unicode MS Regular"]] + ], + 10, + [ + "step", + ["get", "symbolrank"], + ["literal", ["DIN Offc Pro Medium", "Arial Unicode MS Regular"]], + 12, + ["literal", ["DIN Offc Pro Regular", "Arial Unicode MS Regular"]] + ], + 11, + [ + "step", + ["get", "symbolrank"], + ["literal", ["DIN Offc Pro Medium", "Arial Unicode MS Regular"]], + 13, + ["literal", ["DIN Offc Pro Regular", "Arial Unicode MS Regular"]] + ], + 12, + [ + "step", + ["get", "symbolrank"], + ["literal", ["DIN Offc Pro Medium", "Arial Unicode MS Regular"]], + 15, + ["literal", ["DIN Offc Pro Regular", "Arial Unicode MS Regular"]] + ], + 13, + ["literal", ["DIN Offc Pro Medium", "Arial Unicode MS Regular"]] + ], + "text-offset": [ + "step", + ["zoom"], + [ + "match", + ["get", "capital"], + 2, + [ + "match", + ["get", "text_anchor"], + "bottom", + ["literal", [0, -0.3]], + "bottom-left", + ["literal", [0.3, -0.1]], + "left", + ["literal", [0.45, 0.1]], + "top-left", + ["literal", [0.3, 0.1]], + "top", + ["literal", [0, 0.3]], + "top-right", + ["literal", [-0.3, 0.1]], + "right", + ["literal", [-0.45, 0]], + "bottom-right", + ["literal", [-0.3, -0.1]], + ["literal", [0, -0.3]] + ], + [ + "match", + ["get", "text_anchor"], + "bottom", + ["literal", [0, -0.25]], + "bottom-left", + ["literal", [0.2, -0.05]], + "left", + ["literal", [0.4, 0.05]], + "top-left", + ["literal", [0.2, 0.05]], + "top", + ["literal", [0, 0.25]], + "top-right", + ["literal", [-0.2, 0.05]], + "right", + ["literal", [-0.4, 0.05]], + "bottom-right", + ["literal", [-0.2, -0.05]], + ["literal", [0, -0.25]] + ] + ], + 8, + ["literal", [0, 0]] + ], + "text-anchor": ["step", ["zoom"], ["get", "text_anchor"], 8, "center"], + "text-justify": [ + "step", + ["zoom"], + [ + "match", + ["get", "text_anchor"], + ["bottom", "top"], + "center", + ["left", "bottom-left", "top-left"], + "left", + ["right", "bottom-right", "top-right"], + "right", + "center" + ], + 8, + "center" + ], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-max-width": 7, + "text-line-height": 1.1, + "text-size": [ + "interpolate", + ["cubic-bezier", 0.2, 0, 0.9, 1], + ["zoom"], + 3, + [ + "step", + ["get", "symbolrank"], + 12, + 9, + 11, + 10, + 10.5, + 12, + 9.5, + 14, + 8.5, + 16, + 6.5, + 17, + 4 + ], + 15, + [ + "step", + ["get", "symbolrank"], + 28, + 9, + 26, + 10, + 23, + 11, + 21, + 12, + 20, + 13, + 19, + 15, + 17 + ] + ] + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": ["step", ["zoom"], 1, 8, 0], + "text-halo-blur": 1 + } + }, + { + "id": "state-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "place_label", + "minzoom": 3, + "maxzoom": 9, + "filter": ["==", ["get", "class"], "state"], + "layout": { + "text-size": [ + "interpolate", + ["cubic-bezier", 0.85, 0.7, 0.65, 1], + ["zoom"], + 4, + ["step", ["get", "symbolrank"], 10, 6, 9.5, 7, 9], + 9, + ["step", ["get", "symbolrank"], 24, 6, 18, 7, 14] + ], + "text-transform": "uppercase", + "text-font": ["DIN Offc Pro Bold", "Arial Unicode MS Bold"], + "text-field": [ + "step", + ["zoom"], + [ + "step", + ["get", "symbolrank"], + ["coalesce", ["get", "name_en"], ["get", "name"]], + 5, + ["coalesce", ["get", "abbr"], ["get", "name_en"], ["get", "name"]] + ], + 5, + ["coalesce", ["get", "name_en"], ["get", "name"]] + ], + "text-letter-spacing": 0.15, + "text-max-width": 6 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "country-label", + "type": "symbol", + "metadata": {}, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 10, + "filter": ["==", ["get", "class"], "country"], + "layout": { + "icon-image": "dot-11", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-line-height": 1.1, + "text-max-width": 6, + "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Regular"], + "text-anchor": [ + "step", + ["zoom"], + ["coalesce", ["get", "text_anchor"], "center"], + 7, + "center" + ], + "text-offset": [ + "step", + ["zoom"], + [ + "match", + ["get", "text_anchor"], + "bottom", + ["literal", [0, -0.25]], + "bottom-left", + ["literal", [0.2, -0.05]], + "left", + ["literal", [0.4, 0.05]], + "top-left", + ["literal", [0.2, 0.05]], + "top", + ["literal", [0, 0.25]], + "top-right", + ["literal", [-0.2, 0.05]], + "right", + ["literal", [-0.4, 0.05]], + "bottom-right", + ["literal", [-0.2, -0.05]], + ["literal", [0, -0.25]] + ], + 7, + ["literal", [0, 0]] + ], + "text-justify": [ + "step", + ["zoom"], + [ + "match", + ["get", "text_anchor"], + ["bottom", "top"], + "center", + ["left", "bottom-left", "top-left"], + "left", + ["right", "bottom-right", "top-right"], + "right", + "center" + ], + 7, + "center" + ], + "text-size": [ + "interpolate", + ["cubic-bezier", 0.2, 0, 0.7, 1], + ["zoom"], + 1, + ["step", ["get", "symbolrank"], 11, 4, 9, 5, 8], + 9, + ["step", ["get", "symbolrank"], 28, 4, 22, 5, 21] + ] + }, + "paint": { + "icon-opacity": [ + "step", + ["zoom"], + ["case", ["has", "text_anchor"], 1, 0], + 7, + 0 + ], + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": [ + "interpolate", + ["linear"], + ["zoom"], + 2, + "rgba(255,255,255,0.75)", + 3, + "hsl(0, 0%, 100%)" + ], + "text-halo-width": 1.25 + } + } + ], + "visibility": "public", + "created": "1970-01-01T00:00:00.000Z", + "modified": "1970-01-01T00:00:00.000Z", + "owner": "mapbox", + "id": "streets-v11", + "draft": false +} diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 00a1e57..0000000 --- a/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - transform: { - '^.+\\.svelte$': 'svelte-jester', - '^.+\\.js$': 'babel-jest', - }, - moduleFileExtensions: ['js', 'svelte'], - transformIgnorePatterns: ['node_modules/(?!(mapbox-expression)/)'], -}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..54e09a8 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2669 @@ +{ + "name": "chartographer", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "chartographer", + "version": "1.0.0", + "dependencies": { + "maplibre-gl": "^5.24.0" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.63.0", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "gh-pages": "^6.3.0", + "playwright": "^1.61.1", + "sass": "^1.101.0", + "svelte": "^5.56.1", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", + "vite": "^8.0.16" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz", + "integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==", + "license": "MIT", + "engines": { + "node": ">= 22" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz", + "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz", + "integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^4.0.2" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/geojson-vt": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.0.tgz", + "integrity": "sha512-2eIY4gZxeKIVOZVNkAMb+5NgXhgsMQpOveTQAvnp53LYqHGJZDidk7Ew0Tged9PThidpbS+NFTh0g4zivhPDzQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.10.0.tgz", + "integrity": "sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^1.0.0", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^3.0.0", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", + "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/mlt": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.12.tgz", + "integrity": "sha512-ZeK5w2TTeHOajcLaEQs1KZXw2V9wIKo1PmThlxlsHoXsQsYlBqLJzPOd6tJHRtGTChUY3DPPmjXRArYVvAbmZw==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0" + } + }, + "node_modules/@maplibre/vt-pbf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz", + "integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^5.1.0" + } + }, + "node_modules/@maplibre/vt-pbf/node_modules/pbf": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.0.tgz", + "integrity": "sha512-Wv0yo0+uZepnoNEKsquhar1F18LogB8oeEikIhUXG16udbiXG7JecHGySwoo6kuMgjmbQYzdrTZlO+/K9t8eZg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.10.tgz", + "integrity": "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-static": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.68.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.68.0.tgz", + "integrity": "sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.9", + "@types/cookie": "^0.6.0", + "acorn": "^8.16.0", + "cookie": "^0.6.0", + "devalue": "^5.8.1", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/load-config": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz", + "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz", + "integrity": "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "magic-string": "^0.30.21", + "obug": "^2.1.0", + "vitefu": "^1.1.2" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "license": "ISC" + }, + "node_modules/email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.12.tgz", + "integrity": "sha512-On0QbLyaiAkVC4eXtgnXK9Kh2opit+3rcUSOc45DqJ2s/X2eXAHsGOKRSJ6IDagQEW5vPyivANfXUiqgXC67Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gh-pages": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz", + "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.4", + "commander": "^13.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^11.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.8.tgz", + "integrity": "sha512-TM5YqrGeTsVIPPpILzeqZ8D2Zc2TvNgSDi88zPF2a4cyqQdWV/wVWBDRDbNzzrLeRWScrFcOX9lW2iX6GOtUDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kdbush": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", + "license": "ISC" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/maplibre-gl": { + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz", + "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/tiny-sdf": "^2.1.0", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^2.0.4", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/geojson-vt": "^6.1.0", + "@maplibre/maplibre-gl-style-spec": "^24.8.1", + "@maplibre/mlt": "^1.1.8", + "@maplibre/vt-pbf": "^4.3.0", + "@types/geojson": "^7946.0.16", + "earcut": "^3.0.2", + "gl-matrix": "^3.4.4", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^4.0.1", + "potpack": "^2.1.0", + "quickselect": "^3.0.0", + "tinyqueue": "^3.0.0" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pbf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", + "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.137.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.1.tgz", + "integrity": "sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", + "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.10", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.12", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.1.tgz", + "integrity": "sha512-FGUOmAqxXdN/H9Zm8slrqO7SLtFisXRB7rfOsHNJ3MLTD2po/+Stg8XyErkpumPHbuUiYTcqrEIzxpVWKTLqtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "^0.2.0", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/vite": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", + "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "~1.1.2", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json index a0a34bf..03b146a 100644 --- a/package.json +++ b/package.json @@ -1,62 +1,30 @@ { "name": "chartographer", - "version": "1.0.0", "private": true, + "version": "1.0.0", + "type": "module", "scripts": { - "build": "rollup -c", - "dev": "rollup -c -w", - "start": "sirv public --no-clear", - "deploy": "gh-pages -d public", - "prepare": "husky install", - "test": "jest test", - "jest-watch": "jest --watch" - }, - "engines": { - "node": ">=16" + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "deploy": "bash scripts/deploy.sh" }, "devDependencies": { - "@babel/core": "^7.17.9", - "@babel/preset-env": "^7.16.11", - "@rollup/plugin-commonjs": "^17.0.0", - "@rollup/plugin-node-resolve": "^11.0.0", - "@rollup/plugin-replace": "^3.0.0", - "@testing-library/svelte": "^3.1.1", - "babel-jest": "^27.5.1", - "gh-pages": "^3.2.3", - "husky": "^8.0.1", - "jest": "^27.5.1", - "prettier": "^2.7.1", - "prettier-plugin-svelte": "^2.7.0", - "pretty-quick": "^3.1.3", - "rollup": "^2.3.4", - "rollup-plugin-livereload": "^2.0.0", - "rollup-plugin-styles": "^4.0.0", - "rollup-plugin-svelte": "^7.0.0", - "rollup-plugin-terser": "^7.0.0", - "rollup-plugin-web-worker-loader": "^1.6.1", - "svelte": "^3.0.0", - "svelte-jester": "^2.3.2", - "svelte-loading-spinners": "^0.1.7" + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.63.0", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "gh-pages": "^6.3.0", + "playwright": "^1.61.1", + "sass": "^1.101.0", + "svelte": "^5.56.1", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", + "vite": "^8.0.16" }, "dependencies": { - "@fortawesome/free-solid-svg-icons": "^6.0.0", - "@mapbox/mapbox-gl-style-spec": "^13.23.1", - "@maplibre/maplibre-gl-style-spec": "^16.0.0", - "cartesian": "^1.0.1", - "color": "^4.2.3", - "computed-style-to-inline-style": "^3.0.0", - "d3": "^7.1.1", - "file-saver": "^2.0.5", - "html2canvas": "^1.4.1", - "lodash.isequal": "^4.5.0", - "lodash.merge": "^4.6.2", - "lodash.mergewith": "^4.6.2", - "lodash.throttle": "^4.1.1", - "mapbox-expression": "^0.0.3", - "mapbox-gl": "^1.13.2", - "mapbox-gl-style-recurse": "github:stamen/mapbox-gl-style-recurse", - "maplibre-gl": "^2.4.0", - "sirv-cli": "^1.0.0", - "svelte-fa": "^2.4.0" + "maplibre-gl": "^5.24.0" } } diff --git a/public/global.css b/public/global.css deleted file mode 100644 index 72a0d2b..0000000 --- a/public/global.css +++ /dev/null @@ -1,72 +0,0 @@ -html, -body { - position: relative; - width: 100%; - height: 100%; -} - -body { - background: #eee; - color: #333; - margin: 0; - box-sizing: border-box; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; - --app-padding: 12px; -} - -a { - color: rgb(0, 100, 200); - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -a:visited { - color: rgb(0, 80, 160); -} - -label { - display: block; -} - -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - -webkit-padding: 0.4em 0; - padding: 0.4em; - margin: 0 0 0 0; - box-sizing: border-box; - border: 1px solid #ccc; - border-radius: 2px; -} - -input:disabled { - color: #ccc; -} - -button { - color: #333; - background-color: #f4f4f4; - outline: none; - border-radius: 4px; -} - -button:disabled { - color: #999; - pointer-events: none; -} - -button:not(:disabled):active { - background-color: #ddd; -} - -button:hover, -button:focus { - border-color: #666; -} diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 68c87db..0000000 --- a/public/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Chartographer - - - - - - - - - diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index a74344c..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,92 +0,0 @@ -import svelte from 'rollup-plugin-svelte'; -import commonjs from '@rollup/plugin-commonjs'; -import replace from '@rollup/plugin-replace'; -import resolve from '@rollup/plugin-node-resolve'; -import livereload from 'rollup-plugin-livereload'; -import { terser } from 'rollup-plugin-terser'; -import webWorkerLoader from 'rollup-plugin-web-worker-loader'; -import styles from 'rollup-plugin-styles'; - -const production = !process.env.ROLLUP_WATCH; - -function serve() { - let server; - - function toExit() { - if (server) server.kill(0); - } - - return { - writeBundle() { - if (server) return; - server = require('child_process').spawn( - 'npm', - ['run', 'start', '--', '--dev'], - { - stdio: ['ignore', 'inherit', 'inherit'], - shell: true, - } - ); - - process.on('SIGTERM', toExit); - process.on('exit', toExit); - }, - }; -} - -export default { - input: 'src/main.js', - output: { - sourcemap: true, - format: 'iife', - name: 'app', - file: 'public/build/bundle.js', - inlineDynamicImports: true, - }, - plugins: [ - svelte({ - compilerOptions: { - // enable run-time checks when not in production - dev: !production, - }, - }), - - replace({ - 'process.env.NODE_ENV': JSON.stringify( - production ? 'production' : 'development' - ), - }), - - // we'll extract any component CSS out into - // a separate file - better for performance - // Using `styles` instead of `css-only` due to incompatibility - // with web worker: https://github.com/darionco/rollup-plugin-web-worker-loader/issues/60 - styles(['extract', 'bundle.css']), - - // If you have external dependencies installed from - // npm, you'll most likely need these plugins. In - // some cases you'll need additional configuration - - // consult the documentation for details: - // https://github.com/rollup/plugins/tree/master/packages/commonjs - resolve({ - dedupe: ['svelte'], - }), - commonjs(), - - // In dev mode, call `npm run start` once - // the bundle has been generated - !production && serve(), - - // Watch the `public` directory and refresh the - // browser on changes when not in production - !production && livereload('public'), - - // If we're building for production (npm run build - // instead of npm run dev), minify - production && terser(), - webWorkerLoader({}), - ], - watch: { - clearScreen: false, - }, -}; diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..786f2ef --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Builds the static site and publishes it to the gh-pages branch, which +# GitHub Pages serves (custom domain via CNAME — see static/CNAME). +# +# Runs the same way whether invoked directly or from CI, so the two paths +# can't drift apart: +# - Locally: npm run deploy (uses your own git identity/credentials) +# - From CI: .github/workflows/deploy.yml checks out the desired +# branch/ref first, then calls this script — see that file +# if you need to deploy something other than main. + +cd "$(dirname "$0")/.." + +npm run build + +args=(--dist build --branch gh-pages --dotfiles --message "Deploy $(git rev-parse --short HEAD)") + +# CI sets this to a bot identity; a local run falls back to your own git +# config instead of forcing one. +if [ -n "${GIT_DEPLOY_USER:-}" ]; then + args+=(--user "$GIT_DEPLOY_USER") +fi + +npx gh-pages "${args[@]}" diff --git a/scripts/setupTypeScript.js b/scripts/setupTypeScript.js deleted file mode 100644 index 95dda3e..0000000 --- a/scripts/setupTypeScript.js +++ /dev/null @@ -1,134 +0,0 @@ -// @ts-check - -/** This script modifies the project to support TS code in .svelte files like: - - - - As well as validating the code for CI. - */ - -/** To work on this script: - rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template -*/ - -const fs = require('fs'); -const path = require('path'); -const { argv } = require('process'); - -const projectRoot = argv[2] || path.join(__dirname, '..'); - -// Add deps to pkg.json -const packageJSON = JSON.parse( - fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8') -); -packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { - 'svelte-check': '^2.0.0', - 'svelte-preprocess': '^4.0.0', - '@rollup/plugin-typescript': '^8.0.0', - typescript: '^4.0.0', - tslib: '^2.0.0', - '@tsconfig/svelte': '^2.0.0', -}); - -// Add script for checking -packageJSON.scripts = Object.assign(packageJSON.scripts, { - check: 'svelte-check --tsconfig ./tsconfig.json', -}); - -// Write the package JSON -fs.writeFileSync( - path.join(projectRoot, 'package.json'), - JSON.stringify(packageJSON, null, ' ') -); - -// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too -const beforeMainJSPath = path.join(projectRoot, 'src', 'main.js'); -const afterMainTSPath = path.join(projectRoot, 'src', 'main.ts'); -fs.renameSync(beforeMainJSPath, afterMainTSPath); - -// Switch the app.svelte file to use TS -const appSveltePath = path.join(projectRoot, 'src', 'App.svelte'); -let appFile = fs.readFileSync(appSveltePath, 'utf8'); -appFile = appFile.replace(' - -
-
- {#if expandedLayers.length} - - {#if selectedTab !== 'typography'} - - {:else} - - {/if} - {:else} -
- {/if} -
- {#if selectedTab === 'typography' && style} -
- -
- {/if} -
- -
-
-
- {#if expandedLayers.length} - - - {:else if !isLoading} -
-
Drop a style here
-
- {/if} - {#if isLoading} - {#if loadingProgress !== null} -
- -
- {:else} -
- {/if} - {/if} -
- - diff --git a/src/CustomUrlInput.svelte b/src/CustomUrlInput.svelte deleted file mode 100644 index 11a80ea..0000000 --- a/src/CustomUrlInput.svelte +++ /dev/null @@ -1,194 +0,0 @@ - - -
- {#if !!error} -
- {error} -
- {/if} -
- - -
-
- - diff --git a/src/DropArea.svelte b/src/DropArea.svelte deleted file mode 100644 index e5b971f..0000000 --- a/src/DropArea.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - -
- - diff --git a/src/FillsChart.svelte b/src/FillsChart.svelte deleted file mode 100644 index b5b2a8c..0000000 --- a/src/FillsChart.svelte +++ /dev/null @@ -1,318 +0,0 @@ - - -
- - - {#each gradientChunks as gradient (gradient.id)} - - {#each gradient.stops as stop} - - {/each} - - {/each} - - - {#if backgroundRect} - - {/if} - {#each displayChunks as rect, index (rect.layer.id)} - expandDisplayChunks(e.detail)}> - handleClick(rect.layer)} - /> - - {#each rect.layer.id.split('/') as idSection, i} - {#if i === 0 && limitHit.includes(idSection)} - - handleTooltipWarning(idSection, rect.layer.id)} - on:mouseout={handleTooltipClose} - /> - {/if} - 0 ? 18 : 0) + limitHit.includes(idSection) ? 18 : 0} - > - {#if i > 0}↳{/if} - - {idSection} - {/each} - - {/each} - - - - {#each zoomLevels as zoomLevel} - - - {zoomLevel} - - - {/each} - - - - {#if Object.keys(tooltip).length > 0} - - {tooltip.text || ''} - - {/if} -
- - diff --git a/src/LinesChart.svelte b/src/LinesChart.svelte deleted file mode 100644 index bad1e89..0000000 --- a/src/LinesChart.svelte +++ /dev/null @@ -1,441 +0,0 @@ - - -
- - - {#each gradientChunks as gradient (gradient.id)} - - {#each gradient.stops as stop} - - {/each} - - {/each} - - - {#if backgroundRect} - - {/if} - {#each displayChunks as layer, index (layer.id)} - expandDisplayChunks(e.detail)}> - handleClick(layer)} - /> - - {#each layer.id.split('/') as idSection, i} - {#if i === 0 && limitHit.includes(idSection)} - - handleTooltipWarning(idSection, layer.id)} - on:mouseout={handleTooltipClose} - /> - {/if} - 0 ? 18 : 0) + limitHit.includes(idSection) ? 18 : 0} - >{#if i > 0}↳{/if}{idSection} - - {/each} - - - {/each} - - - - {#each zoomLevels as zoomLevel} - - - {zoomLevel} - - - {/each} - - - - {#if Object.keys(tooltip).length > 0} - - {tooltip.text || ''} - - {/if} -
- - diff --git a/src/ProgressBar.svelte b/src/ProgressBar.svelte deleted file mode 100644 index 74ce8a5..0000000 --- a/src/ProgressBar.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - -
-
-
Loading ...
-
-
-
-
- - diff --git a/src/RendererSelect.svelte b/src/RendererSelect.svelte deleted file mode 100644 index df376c9..0000000 --- a/src/RendererSelect.svelte +++ /dev/null @@ -1,102 +0,0 @@ - - -
-
-
- Renderer: -
`• ${e}`).join('\n\n') : ''} - > - -
-
- - -
- - diff --git a/src/SlotWrapper.svelte b/src/SlotWrapper.svelte deleted file mode 100644 index dcebd0f..0000000 --- a/src/SlotWrapper.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/src/Tab.svelte b/src/Tab.svelte deleted file mode 100644 index 7cb065c..0000000 --- a/src/Tab.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - -
- {label[0].toUpperCase() + label.substring(1)} -
- - diff --git a/src/Tabs.svelte b/src/Tabs.svelte deleted file mode 100644 index 103c5f5..0000000 --- a/src/Tabs.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - -
- - - -
- - diff --git a/src/TabsContent.svelte b/src/TabsContent.svelte deleted file mode 100644 index 927da59..0000000 --- a/src/TabsContent.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -
- {#if selectedTab === 'fill'} - - {:else if selectedTab === 'lines'} - - {:else if selectedTab === 'typography'} - {#key rendererLib} - - {/key} - {/if} -
- - diff --git a/src/Tooltip.svelte b/src/Tooltip.svelte deleted file mode 100644 index 980afea..0000000 --- a/src/Tooltip.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - -
- {#if showCloseButton} - - {/if} -
-    
-      
-    
-  
-
- - diff --git a/src/TypographyChart.svelte b/src/TypographyChart.svelte deleted file mode 100644 index 7240d50..0000000 --- a/src/TypographyChart.svelte +++ /dev/null @@ -1,549 +0,0 @@ - - -
-
- - {#if Object.keys(tooltip).length > 0} - - {#if !editMode} -
- -
- {/if} - {#if editMode} -
- -
- {/if} -

{tooltip.text || ''}

-
- {/if} - {#if Object.keys(hoverTooltip).length > 0} - - {hoverTooltip.text || ''} - - {/if} -
- - diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..da08e6d --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..6a2bb58 --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/constants.js b/src/constants.js deleted file mode 100644 index 76c1b1f..0000000 --- a/src/constants.js +++ /dev/null @@ -1,20 +0,0 @@ -export let MIN_ZOOM = 0; -export let MAX_ZOOM = 24; -export let CHART_WIDTH = 1000; -export let MARGIN = { - bottom: 25, - left: 250, - right: 50, - top: 25, -}; -export const DISPLAY_CHUNK_SIZE = 100; -export const renderers = [ - { - name: 'Mapbox GL', - value: 'mapbox-gl', - }, - { - name: 'Maplibre GL', - value: 'maplibre-gl', - }, -]; diff --git a/src/convert-colors.js b/src/convert-colors.js deleted file mode 100644 index 27727a9..0000000 --- a/src/convert-colors.js +++ /dev/null @@ -1,73 +0,0 @@ -// Converts all color values in stylesheet to RGB -import Color from 'color'; -import { latest } from '@mapbox/mapbox-gl-style-spec'; -import mapboxGlStyleRecurse from 'mapbox-gl-style-recurse'; - -const createRecurseStyle = mapboxGlStyleRecurse.createRecurseStyle; - -// Color properties from the style spec in case new ones are added -const convertToRgbPropertyIds = Object.keys(latest).reduce((acc, k) => { - if (k.includes('paint')) { - Object.keys(latest[k]).forEach(propertyId => { - if (latest[k][propertyId]['type'] === 'color') { - acc.push(propertyId); - } - }); - } - return acc; -}, []); - -const expressionNames = Object.keys(latest['expression_name']['values']); - -/** - * convertToRgb - * Convert colors to RGB (rounded off decimal points) - * @param {string} color - color string to be converted - * @returns {string} - color as rgb value - */ -const convertToRgb = (id, color, key) => { - // Hue might not be necessary, but basically we want to avoid rounding alpha - const attrToRound = ['r', 'g', 'b', 'a']; - - let rgbObj = Color(color).rgb().object(); - - rgbObj = Object.keys(rgbObj).reduce((acc, k) => { - acc[k] = attrToRound.includes(k) ? Math.round(rgbObj[k]) : rgbObj[k]; - return acc; - }, {}); - - const rgbcolor = Color(rgbObj).rgb().string(); - - return rgbcolor; -}; - -/** - * convertToRgbCondition - * Condition to run convertToRgb on - * @param {Array} val - property value - * @returns {boolean} - true or false - */ -const convertToRgbCondition = (val, key) => { - if (typeof val !== 'string') return false; - if (expressionNames.includes(val)) return false; - try { - // Checks if it is a valid color string - new Color(val); - } catch (e) { - return false; - } - return true; -}; - -const convertStylesheetToRgb = createRecurseStyle({ - transformFn: convertToRgb, - transformCondition: convertToRgbCondition, - propertyIds: convertToRgbPropertyIds, -}); - -export { - convertStylesheetToRgb, - convertToRgb, - convertToRgbCondition, - convertToRgbPropertyIds, -}; diff --git a/src/expand-layers-worker.js b/src/expand-layers-worker.js deleted file mode 100644 index 5a39a9e..0000000 --- a/src/expand-layers-worker.js +++ /dev/null @@ -1,46 +0,0 @@ -import { expandLayer } from './styles/expandLayers'; -import throttle from 'lodash.throttle'; - -const throttleTime = 250; - -self.addEventListener( - 'message', - e => { - const layers = e.data; - let limitedExpandedLayerIds = []; - - let progress = 0; - const throttledProgressUpdate = throttle(() => { - self.postMessage({ progress }); - }, throttleTime); - - const expandLayers = layers => { - let nextLayers = layers; - self.postMessage({ progress }); - try { - nextLayers = layers.reduce((acc, l, i) => { - const { expandedLayers, comboLimitHit } = expandLayer(l); - if (comboLimitHit) limitedExpandedLayerIds.push(l.id); - acc = acc.concat(expandedLayers); - progress = i / layers.length; - throttledProgressUpdate(); - return acc; - }, []); - } catch (err) { - console.error(err); - } - setTimeout( - () => - self.postMessage({ - progress, - expandedLayers: nextLayers, - limitedExpandedLayerIds, - }), - throttleTime - ); - }; - - expandLayers(layers); - }, - false -); diff --git a/src/fetch-url.js b/src/fetch-url.js deleted file mode 100644 index c539a88..0000000 --- a/src/fetch-url.js +++ /dev/null @@ -1,7 +0,0 @@ -const fetchUrl = async url => { - const response = await fetch(url); - const data = await response.json(); - return data; -}; - -export { fetchUrl }; diff --git a/src/gather-outputs.js b/src/gather-outputs.js deleted file mode 100644 index 3f88736..0000000 --- a/src/gather-outputs.js +++ /dev/null @@ -1,59 +0,0 @@ -// Gathers outputs of expression based on expression type -const gatherOutputs = exp => { - // Prevent unwanted mutations - const expression = JSON.parse(JSON.stringify(exp)); - if (typeof expression === 'number') return [expression]; - const outputs = []; - const expressionType = expression.hasOwnProperty('stops') - ? 'legacy' - : expression[0]; - - switch (expressionType) { - case 'case': { - let inputOutputs = expression.slice(1); - const fallback = inputOutputs.pop(); - inputOutputs.forEach((val, i) => i % 2 !== 0 && outputs.push(val)); - outputs.push(fallback); - break; - } - case 'match': { - let inputOutputs = expression.slice(2); - const fallback = inputOutputs.pop(); - inputOutputs.forEach((val, i) => i % 2 !== 0 && outputs.push(val)); - outputs.push(fallback); - break; - } - case 'interpolate': { - let inputOutputs = expression.slice(3); - inputOutputs.forEach((val, i) => i % 2 !== 0 && outputs.push(val)); - break; - } - case 'step': { - let inputOutputs = expression.slice(2); - const fallback = inputOutputs.pop(); - inputOutputs.forEach((val, i) => i % 2 === 0 && outputs.push(val)); - outputs.push(fallback); - break; - } - case 'legacy': { - let inputOutputs = expression.stops; - inputOutputs.forEach(val => outputs.push(val[1])); - break; - } - default: - return expression; - } - if (outputs.some(item => Array.isArray(item))) { - return outputs.reduce((acc, item) => { - if (Array.isArray(item)) { - acc = acc.concat(gatherOutputs(item)); - } else { - acc = acc.concat([item]); - } - return acc; - }, []); - } - return outputs; -}; - -export { gatherOutputs }; diff --git a/src/get-color.js b/src/get-color.js deleted file mode 100644 index 9e76efe..0000000 --- a/src/get-color.js +++ /dev/null @@ -1,223 +0,0 @@ -import { getValue as getInterpolatedValue } from './interpolation'; -import { MIN_ZOOM, MAX_ZOOM } from './constants'; - -// Literal expressions are unnecessary but valid for opacity and color -// so we remove them for things to run smoothly -const handleLiteral = value => { - if (Array.isArray(value) && value[0] === 'literal') { - return value[1]; - } - if (Array.isArray(value)) { - return value.map(handleLiteral); - } - return value; -}; - -const isGradient = value => { - if (!Array.isArray(value)) return false; - - const expressionType = value[0]; - - switch (expressionType) { - case 'step': { - const [, [attribute]] = value; - if (attribute === 'zoom') { - return true; - } - break; - } - case 'interpolate': { - const [, [interpolationType], [attribute]] = value; - if (interpolationType === 'linear' && attribute === 'zoom') { - return true; - } - break; - } - default: - return false; - } -}; - -const getZoomOutputArray = scaleExpression => { - if (!isGradient(scaleExpression)) return []; - const expressionType = scaleExpression[0]; - - if (expressionType === 'interpolate') return scaleExpression.slice(3); - - // For the sake of consistent gradient handling, convert step functions - // to look more like interpolate functions - let stepZoomOutput = scaleExpression.slice(2); - stepZoomOutput.unshift(0.1); - - stepZoomOutput = stepZoomOutput.reduce((acc, item, i) => { - // zoom - if (i % 2 === 0 && i !== 0) { - acc.push(Number(item) - 0.01, stepZoomOutput[i - 1]); - acc.push(item); - } // output - else { - acc.push(item); - } - - return acc; - }, []); - - return stepZoomOutput; -}; - -const getGradientStops = ({ - layerId, - color, - opacity, - minZoom, - maxZoom, - xScale, -}) => { - const colorInterpolation = isGradient(color) && color[0]; - const opacityInterpolation = isGradient(opacity) && opacity[0]; - - const gradientStops = []; - - if (colorInterpolation || opacityInterpolation) { - const lineStart = xScale(minZoom); - const lineLength = xScale(maxZoom) - xScale(minZoom); - - opacity = opacity; - let gradientColorArray = getZoomOutputArray(color); - let gradientOpacityArray = getZoomOutputArray(opacity); - - const stops = gradientColorArray - .concat(gradientOpacityArray) - .filter((v, i) => i % 2 === 0) - .sort((a, b) => a - b); - - stops.forEach(zoomStop => { - let colorOutput = colorInterpolation - ? getInterpolatedValue(color, zoomStop, null) - : color; - let opacityOutput = opacityInterpolation - ? getInterpolatedValue(opacity, zoomStop, null) - : opacity; - - if (Array.isArray(colorOutput)) { - console.warn( - `${layerId}: Unhandled data expression, ${JSON.stringify( - colorOutput, - null, - 2 - )}` - ); - colorOutput = 'black'; - } - if (Array.isArray(opacityOutput)) { - console.warn( - `${layerId}: Unhandled data expression, ${JSON.stringify( - opacityOutput, - null, - 2 - )}` - ); - opacityOutput = 1; - } - opacityOutput = parseFloat(opacityOutput.toFixed(2)); - - gradientStops.push({ - offset: ((xScale(zoomStop) - lineStart) / lineLength) * 100, - stopColor: colorOutput, - stopOpacity: opacityOutput, - }); - }); - } - - return gradientStops; -}; - -const getValidGradientId = id => { - const invalidGradientUrlChars = ['"', ' ', "'"]; - - let nextId = id; - for (const char of invalidGradientUrlChars) { - nextId = nextId.replaceAll(char, ''); - } - return nextId; -}; - -const getColor = (layer, xScale) => { - let gradients = []; - const minZoom = layer.minzoom || MIN_ZOOM; - const maxZoom = layer.maxzoom || MAX_ZOOM; - - let color; - let opacity; - let strokeColor; - let strokeWidth = 0; - - // Return early for hidden layers - const isNotVisible = layer?.layout?.visibility === 'none'; - if (isNotVisible) { - return { - gradients, - color: { - color: 'rgba(0, 0, 0, 0)', - strokeColor: 'rgba(0, 0, 0, 0)', - strokeWidth, - }, - }; - } - - switch (layer.type) { - case 'fill': - color = layer?.paint?.['fill-color']; - opacity = layer?.paint?.['fill-opacity']; - strokeColor = layer?.paint?.['fill-outline-color']; - if (strokeColor) strokeWidth = 1; - break; - case 'background': - color = layer?.paint?.['background-color']; - break; - case 'line': - color = layer?.paint?.['line-color']; - opacity = layer?.paint?.['line-opacity']; - break; - case 'symbol': - color = layer?.paint?.['text-color']; - opacity = layer?.paint?.['text-opacity']; - break; - default: { - } - } - - // Defaults - color = handleLiteral(color) || 'black'; - opacity = opacity !== undefined ? handleLiteral(opacity) : 1; - - const gradientStops = getGradientStops({ - layerId: layer.id, - color, - opacity, - minZoom, - maxZoom, - xScale, - }); - - if (gradientStops.length > 1) { - const gradientId = getValidGradientId(layer.id); - - gradients = [ - { - id: gradientId, - stops: gradientStops.map(stop => ({ - offset: `${stop.offset}%`, - stopColor: stop.stopColor, - stopOpacity: stop.stopOpacity, - })), - }, - ]; - - color = `url(#${gradientId})`; - } - - return { gradients, color: { color, strokeColor, strokeWidth } }; -}; - -export { getColor }; diff --git a/src/interpolation/index.js b/src/interpolation/index.js deleted file mode 100644 index 34660ca..0000000 --- a/src/interpolation/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { getLinearFunction } from './linear'; -import { getStepFunction } from './step'; - -export function getFunction(expression) { - const [expressionType] = expression; - if (expressionType === 'step') return getStepFunction(expression); - if (expressionType === 'interpolate') { - const interpolationType = expression[1][0]; - if (interpolationType === 'linear') { - return getLinearFunction(expression); - } - } - return () => {}; -} - -export function getValue(expression, zoom, defaultValue = null) { - if (expression == undefined) return defaultValue; - if (typeof expression === 'number') return expression; - if (Array.isArray(expression)) { - return getFunction(expression)(zoom); - } - return defaultValue; -} diff --git a/src/interpolation/linear.js b/src/interpolation/linear.js deleted file mode 100644 index f18f797..0000000 --- a/src/interpolation/linear.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as d3 from 'd3'; - -export function getLinearFunction(expression) { - const [expressionType, [interpolationType], [input], ...stops] = expression; - return value => { - if (value < stops[0]) return stops[1]; - if (value > stops[stops.length - 2]) return stops[stops.length - 1]; - return d3.scaleLinear( - stops.filter((e, i) => i % 2 === 0), - stops.filter((e, i) => i % 2 === 1) - )(value); - }; -} diff --git a/src/interpolation/step.js b/src/interpolation/step.js deleted file mode 100644 index ba8b779..0000000 --- a/src/interpolation/step.js +++ /dev/null @@ -1,11 +0,0 @@ -export function getStepFunction(expression) { - return zoom => { - const steps = expression.slice(2).reverse(); - for (let i = 0; i < steps.length; i++) { - const currentValue = steps[i]; - const currentZoom = steps[i + 1] || 0; - if (zoom >= currentZoom) return currentValue; - } - return steps[0]; - }; -} diff --git a/src/is-access-token-required.js b/src/is-access-token-required.js deleted file mode 100644 index 90fd935..0000000 --- a/src/is-access-token-required.js +++ /dev/null @@ -1,24 +0,0 @@ -const isMapboxUrl = url => { - if (typeof url !== 'string') return false; - const hasMapboxFormat = url.startsWith('mapbox://'); - return hasMapboxFormat; -}; - -const isAccessTokenRequired = style => { - const { sprite, glyphs, sources } = style; - - const hasMapboxSourceUrls = !!Object.values(sources) - .reduce((acc, s) => { - if (s.url) return acc.concat(s.url); - if (s.tiles) return acc.concat(s.tiles); - return acc; - }, []) - .filter(isMapboxUrl).length; - - const usesMapboxUrls = - isMapboxUrl(sprite) || isMapboxUrl(glyphs) || hasMapboxSourceUrls; - - return usesMapboxUrls; -}; - -export { isAccessTokenRequired }; diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/components/BackgroundSwatch.svelte b/src/lib/components/BackgroundSwatch.svelte new file mode 100644 index 0000000..ab77b05 --- /dev/null +++ b/src/lib/components/BackgroundSwatch.svelte @@ -0,0 +1,92 @@ + + +
+ {@render children()} +
+ + diff --git a/src/lib/components/DataConfigModal.svelte b/src/lib/components/DataConfigModal.svelte new file mode 100644 index 0000000..7f957b0 --- /dev/null +++ b/src/lib/components/DataConfigModal.svelte @@ -0,0 +1,256 @@ + + + + +{#if open} + +{/if} + + diff --git a/src/lib/components/LayerBar.svelte b/src/lib/components/LayerBar.svelte new file mode 100644 index 0000000..f5e7b63 --- /dev/null +++ b/src/lib/components/LayerBar.svelte @@ -0,0 +1,198 @@ + + +
+
{label}
+ +
+ {#if dataUrl} + + {:else if renderError} + âš  render error + {:else} + + {/if} +
+
+ + diff --git a/src/lib/components/SpriteViewerModal.svelte b/src/lib/components/SpriteViewerModal.svelte new file mode 100644 index 0000000..b15e061 --- /dev/null +++ b/src/lib/components/SpriteViewerModal.svelte @@ -0,0 +1,166 @@ + + + + +{#if open} + +{/if} + + diff --git a/src/lib/components/StyleDropzone.svelte b/src/lib/components/StyleDropzone.svelte new file mode 100644 index 0000000..dabf8e5 --- /dev/null +++ b/src/lib/components/StyleDropzone.svelte @@ -0,0 +1,127 @@ + + + +
+
+

Chartographer

+

Drop a Maplibre style JSON to get started

+ + {#if errorMessage} +

{errorMessage}

+ {/if} + + + +
+
+ + diff --git a/src/lib/dataFieldCollector.ts b/src/lib/dataFieldCollector.ts new file mode 100644 index 0000000..6d9c974 --- /dev/null +++ b/src/lib/dataFieldCollector.ts @@ -0,0 +1,152 @@ +import type { StyleSpecification } from 'maplibre-gl'; + +// --------------------------------------------------------------------------- +// Data field collector +// +// Walks every layer's paint + layout expressions and harvests every +// "match"/"case" branch keyed on a feature property (["get", field]), +// recording which literal values the style treats as meaningful for that +// field — grouped by layer type so each visualization page (fills, lines, +// ...) only sees the fields relevant to layers it actually renders. +// +// This does NOT try to preserve which value maps to which output. We only +// need the domain of values so a user can pick one and let the real +// renderer evaluate the full expression against synthetic features carrying +// that value — see expressionRewriter.ts for why that's simpler than trying +// to statically reconstruct each branch's output. +// --------------------------------------------------------------------------- + +type ExprNode = string | number | boolean | null | ExprNode[]; + +export type DataFieldValue = string | number | boolean; + +export interface DataFieldsByType { + [layerType: string]: { + [field: string]: DataFieldValue[]; + }; +} + +function isPrimitive(node: ExprNode): node is DataFieldValue { + return typeof node === 'string' || typeof node === 'number' || typeof node === 'boolean'; +} + +/** Returns the property name if `node` is exactly a `["get", field]` expression. */ +function getField(node: ExprNode): string | null { + if (Array.isArray(node) && node[0] === 'get' && typeof node[1] === 'string') { + return node[1]; + } + return null; +} + +function addValue(result: DataFieldsByType, layerType: string, field: string, value: DataFieldValue) { + const byField = (result[layerType] ??= {}); + const values = (byField[field] ??= []); + if (!values.includes(value)) values.push(value); +} + +/** Harvests a `match` node's field and every branch label (the fallback is skipped — it's not a value, it's a default output). */ +function harvestMatch(node: ExprNode[], layerType: string, result: DataFieldsByType) { + const field = getField(node[1]); + if (field === null) return; + + // Layout: ["match", input, label1, output1, label2, output2, ..., fallback]. + // Pairs run from index 2 up to (not including) the trailing fallback. + for (let i = 2; i < node.length - 1; i += 2) { + const label = node[i]; + const labels = Array.isArray(label) ? label : [label]; + for (const l of labels) { + if (isPrimitive(l)) addValue(result, layerType, field, l); + } + } +} + +/** Harvests field/value pairs referenced by a single `case` condition expression. */ +function harvestCondition(cond: ExprNode, layerType: string, result: DataFieldsByType) { + if (!Array.isArray(cond)) return; + const [op, ...args] = cond; + + if (op === '==' || op === '!=') { + const [a, b] = args; + const fieldA = getField(a); + const field = fieldA ?? getField(b); + if (field === null) return; + const value = fieldA !== null ? b : a; + if (isPrimitive(value)) addValue(result, layerType, field, value); + return; + } + + if (op === 'in') { + const field = getField(args[0]); + if (field === null) return; + // Legacy variadic form: ["in", ["get", f], v1, v2, ...]. + let values = args.slice(1); + // Modern form: ["in", ["get", f], ["literal", [...]]] (or a raw array). + if (values.length === 1 && Array.isArray(values[0])) { + const inner = values[0]; + values = inner[0] === 'literal' ? (inner[1] as ExprNode[]) : inner; + } + for (const v of values) { + if (isPrimitive(v)) addValue(result, layerType, field, v); + } + return; + } + + if (op === 'all' || op === 'any') { + for (const sub of args) harvestCondition(sub, layerType, result); + return; + } + + if (op === '!') { + harvestCondition(args[0], layerType, result); + } + + // Anything else (>, <, has, !has, a bare ["get", field] truthy check, ...) + // has no discrete literal to harvest — skip rather than guess. +} + +/** Harvests every condition in a `case` node (the trailing fallback is skipped). */ +function harvestCase(node: ExprNode[], layerType: string, result: DataFieldsByType) { + // Layout: ["case", cond1, output1, cond2, output2, ..., fallback]. + for (let i = 1; i < node.length - 1; i += 2) { + harvestCondition(node[i], layerType, result); + } +} + +function walk(node: ExprNode, layerType: string, result: DataFieldsByType) { + if (!Array.isArray(node)) return; + + if (node[0] === 'match') harvestMatch(node, layerType, result); + else if (node[0] === 'case') harvestCase(node, layerType, result); + + // Always recurse into every child regardless of the branch above — a + // match/case can be nested anywhere (e.g. as one stop's value inside an + // interpolate over zoom), and its own labels/outputs/conditions may + // themselves contain further match/case nodes. + for (const child of node) walk(child, layerType, result); +} + +/** + * Walks an entire style's paint + layout blocks and collects every + * match/case branch value, grouped by layer type. + */ +export function collectDataFields(style: StyleSpecification): DataFieldsByType { + const result: DataFieldsByType = {}; + + for (const layer of style.layers) { + const layerType = layer.type; + const paint = (layer as { paint?: Record }).paint; + const layout = (layer as { layout?: Record }).layout; + + for (const value of Object.values(paint ?? {})) walk(value, layerType, result); + for (const value of Object.values(layout ?? {})) walk(value, layerType, result); + } + + // Sort each field's values for stable, predictable UI ordering. + for (const fields of Object.values(result)) { + for (const key of Object.keys(fields)) { + fields[key].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); + } + } + + return result; +} diff --git a/src/lib/expressionRewriter.ts b/src/lib/expressionRewriter.ts new file mode 100644 index 0000000..bdce38b --- /dev/null +++ b/src/lib/expressionRewriter.ts @@ -0,0 +1,142 @@ +// --------------------------------------------------------------------------- +// Expression rewriter: replaces every ["zoom"] node in a MapLibre +// paint/layout expression tree with ["get", "zoom"]. +// +// Why: +// In a real map render, ["zoom"] returns the camera's current zoom level. +// We replace it with ["get", "zoom"] so it instead reads a "zoom" property +// from each GeoJSON feature. Our fake features each carry a "zoom" value +// representing the zoom level they visually stand for, so the renderer +// evaluates the expression as if the map were at that zoom — giving us the +// correct color/width at every point along the horizontal bar. +// +// Example transform: +// ["interpolate", ["linear"], ["zoom"], 5, "#ff0000", 10, "#0000ff"] +// becomes → +// ["interpolate", ["linear"], ["get", "zoom"], 5, "#ff0000", 10, "#0000ff"] +// +// Both forms are valid MapLibre expressions. The second is data-driven, so it +// can appear anywhere in the expression tree (unlike the camera-only ["zoom"]). +// --------------------------------------------------------------------------- + +/** Any node that can appear in a MapLibre expression tree. */ +type ExprNode = string | number | boolean | null | ExprNode[]; + +/** + * Walk an expression tree and replace every ["zoom"] leaf with ["get", "zoom"]. + * Returns the original value unchanged when there are no ["zoom"] nodes. + */ +export function rewriteZoom(expr: ExprNode): ExprNode { + // Primitives have no nested structure — return as-is. + if (!Array.isArray(expr)) return expr; + + // ["zoom"] is the one-element zoom camera expression; swap it out. + if (expr.length === 1 && expr[0] === 'zoom') { + return ['get', 'zoom']; + } + + // Recurse into every child element of the array expression. + return expr.map(rewriteZoom); +} + +/** + * Rewrite all zoom expressions across an entire paint block. + * Returns a new object; the original is not mutated. + */ +export function rewritePaint(paint: Record): Record { + const result: Record = {}; + for (const [prop, value] of Object.entries(paint)) { + result[prop] = rewriteZoom(value); + } + return result; +} + +// --------------------------------------------------------------------------- +// text-field rewriter: replaces every ["get", field] that PRODUCES TEXT +// CONTENT with the literal string field, since we have no real data for +// arbitrary label fields (unlike match/case data, which the user configures +// via styleStore.dataConfig) — showing the field name lets a user see which +// property drives the label. +// +// ["get", ...] used inside a case/match CONDITION (deciding which branch to +// take, e.g. picking a name field based on ["get", "class"]) is left +// untouched — those should keep evaluating against the real, configurable +// feature properties, exactly like fill/line layers. +// +// Example transform: +// ["get", "name_en"] becomes -> "name_en" +// ["match", ["get","class"], +// "country", ["get","name_en"], becomes -> ["match", ["get","class"], +// ["get","name"]] "country", "name_en", +// "name"] +// (the match's own input stays a real ["get","class"] — only the outputs, +// which produce the displayed text, become literal field names) +// --------------------------------------------------------------------------- + +/** A node that can appear in a text-field expression — broader than + * ExprNode since "format" sections carry plain option objects too. */ +type FieldNode = string | number | boolean | null | Record | FieldNode[]; + +function isFieldArray(node: FieldNode): node is FieldNode[] { + return Array.isArray(node); +} + +export function rewriteTextField(expr: FieldNode): FieldNode { + if (!isFieldArray(expr)) return expr; + + const op = expr[0]; + + if (op === 'get' && typeof expr[1] === 'string') { + return expr[1]; + } + + if (op === 'case') { + // ["case", cond1, out1, cond2, out2, ..., fallback] — conditions are + // left untouched; outputs and the fallback are content. + const result: FieldNode[] = ['case']; + for (let i = 1; i < expr.length - 1; i += 2) { + result.push(expr[i], rewriteTextField(expr[i + 1] as FieldNode)); + } + result.push(rewriteTextField(expr[expr.length - 1] as FieldNode)); + return result; + } + + if (op === 'match') { + // ["match", input, label1, out1, label2, out2, ..., fallback] — the + // input being matched is left untouched (real, configurable data); + // labels are already literals; outputs and the fallback are content. + const result: FieldNode[] = ['match', expr[1] as FieldNode]; + for (let i = 2; i < expr.length - 1; i += 2) { + result.push(expr[i], rewriteTextField(expr[i + 1] as FieldNode)); + } + result.push(rewriteTextField(expr[expr.length - 1] as FieldNode)); + return result; + } + + if (op === 'step') { + // ["step", input, output0, stop1, output1, ...] — input (typically + // ["zoom"], already handled by rewriteZoom) is left untouched; every + // output is content, stops are plain numbers. + const result: FieldNode[] = ['step', expr[1] as FieldNode, rewriteTextField(expr[2] as FieldNode)]; + for (let i = 3; i < expr.length; i += 2) { + result.push(expr[i]); + if (i + 1 < expr.length) result.push(rewriteTextField(expr[i + 1] as FieldNode)); + } + return result; + } + + if (op === 'format') { + // ["format", part1, opts1, part2, opts2, ...] — every part is + // content; the options objects between them are left untouched. + const result: FieldNode[] = ['format']; + for (let i = 1; i < expr.length; i += 2) { + result.push(rewriteTextField(expr[i] as FieldNode)); + if (i + 1 < expr.length) result.push(expr[i + 1]); + } + return result; + } + + // Any other wrapper (concat, coalesce, to-string, upcase, downcase, ...) + // — every child is a content position. + return expr.map(rewriteTextField); +} diff --git a/src/lib/geojson.ts b/src/lib/geojson.ts new file mode 100644 index 0000000..7a4ed77 --- /dev/null +++ b/src/lib/geojson.ts @@ -0,0 +1,178 @@ +import type { FeatureCollection, Feature, Polygon, LineString, Point } from 'geojson'; +import type { DataFieldValue } from './dataFieldCollector'; + +// --------------------------------------------------------------------------- +// Fake GeoJSON builders +// +// We visualize a style's zoom-level behavior by creating fake features +// arranged horizontally across the full longitude range: +// +// longitude -180 → zoom 0 +// longitude +180 → zoom 22 +// +// Each feature carries a "zoom" property equal to its zoom level. +// After paint expressions are rewritten (["zoom"] → ["get", "zoom"]), +// the renderer evaluates the style against this property, showing how +// the layer looks at every zoom level across the bar. +// --------------------------------------------------------------------------- + +/** Step size between zoom segments. 0.1 → 220 segments across 0–22. */ +const ZOOM_STEP = 0.1; +const ZOOM_MAX = 22; +const SEGMENT_COUNT = Math.round(ZOOM_MAX / ZOOM_STEP); // 220 + +/** + * Map a zoom level (0–22) to a longitude, stopping just short of +180 + * rather than reaching it exactly. MapLibre normalizes longitude to + * [-180, 180) — +180 wraps down to exactly -180 — so a point or segment + * boundary placed at true +180 renders on top of the zoom-0 one instead of + * at the right edge. Only ~0.01° short of the true edge, imperceptible at + * this scale. + */ +function zoomToLon(zoom: number): number { + return -180 + (zoom / ZOOM_MAX) * 359.99; +} + +/** + * Build fill GeoJSON: 220 vertical rectangle strips spanning the full + * Mercator latitude extent (-85.05 to +85.05). + * + * Using the full latitude range ensures the polygons fill the bar height + * regardless of the container's aspect ratio. + * + * The "zoom" property on each feature drives fill-color after expression + * rewriting. `dataConfig` adds any user-chosen data field overrides (see + * DataConfigModal) so match/case expressions in the style evaluate against + * real values instead of always hitting their fallback branch. + */ +export function buildFillGeoJSON( + dataConfig: Record = {} +): FeatureCollection { + const features: Feature[] = []; + + for (let i = 0; i < SEGMENT_COUNT; i++) { + // Round to avoid floating-point noise (e.g. 0.30000000000000004) + const zoom = parseFloat((i * ZOOM_STEP).toFixed(2)); + const lonA = zoomToLon(zoom); + const lonB = zoomToLon(zoom + ZOOM_STEP); + + features.push({ + type: 'Feature', + properties: { zoom, ...dataConfig }, + geometry: { + type: 'Polygon', + coordinates: [ + [ + [lonA, -85.05], + [lonB, -85.05], + [lonB, 85.05], + [lonA, 85.05], + [lonA, -85.05] // GeoJSON rings must be closed + ] + ] + } + }); + } + + return { type: 'FeatureCollection', features }; +} + +/** + * Build line GeoJSON: 220 short horizontal line segments along the equator. + * + * Each segment spans one zoom step in longitude. The "zoom" property drives + * line-color and line-width after expression rewriting, making the line + * visually thicker/thinner and change color from left to right as zoom grows. + * `dataConfig` adds any user-chosen data field overrides (see + * DataConfigModal) so match/case expressions in the style evaluate against + * real values instead of always hitting their fallback branch. + */ +export function buildLineGeoJSON( + dataConfig: Record = {} +): FeatureCollection { + const features: Feature[] = []; + + for (let i = 0; i < SEGMENT_COUNT; i++) { + const zoom = parseFloat((i * ZOOM_STEP).toFixed(2)); + const lonA = zoomToLon(zoom); + const lonB = zoomToLon(zoom + ZOOM_STEP); + + features.push({ + type: 'Feature', + properties: { zoom, ...dataConfig }, + geometry: { + type: 'LineString', + // A single short segment along latitude 0 (the equator) + coordinates: [ + [lonA, 0], + [lonB, 0] + ] + } + }); + } + + return { type: 'FeatureCollection', features }; +} + +/** Step size between symbol samples — every 2 zoom levels (12 points across + * 0–22), much sparser than fill/line's 220 segments, since labels need real + * horizontal room or every sample would overlap into an illegible smear. */ +const SYMBOL_ZOOM_STEP = 2; + +/** + * Fraction of the bar width reserved as empty margin on each side, for + * symbols only. Text labels are usually center-anchored, so a label at + * zoom 0 or 22 (placed exactly at the world edge, like fill/line's content) + * would have roughly half its width extending past the edge — and + * MapLibre's symbol placement, observed directly, doesn't just clip that + * overflow: it can wrap-place the label onto the *opposite* edge instead + * (the antimeridian-equivalent position), since a label anchored right at + * the edge is ambiguous between the two. Insetting zoom 0/22 away from the + * true edges avoids that ambiguity. 0.15 leaves the middle 70% of the bar + * for content — matches the zoom-ruler tick positions in +page.svelte. + */ +export const SYMBOL_MARGIN_FRACTION = 0.15; + +/** + * Target pixel spacing between adjacent zoom samples on the symbols page. + * Unlike fill/line, symbol bars use a fixed render width (not the viewport + * width) and scroll horizontally — labels need real, consistent room + * regardless of window size, or wide labels at high zoom crowd into their + * neighbors. Adjust this single value if a style still crowds (larger + * text-size) or feels too sparse; the symbols page derives its total render + * width from it. + */ +export const SYMBOL_SAMPLE_SPACING_PX = 150; + +function symbolZoomToLon(zoom: number): number { + const usableSpan = 359.99 * (1 - 2 * SYMBOL_MARGIN_FRACTION); + const startLon = -180 + 359.99 * SYMBOL_MARGIN_FRACTION; + return startLon + (zoom / ZOOM_MAX) * usableSpan; +} + +/** + * Build symbol GeoJSON: one point every 2 zoom levels along the equator, + * inset from the bar's true edges by SYMBOL_MARGIN_FRACTION (see above). + * + * The "zoom" property on each feature drives text-size/icon-size (and + * anything else zoom-dependent) after expression rewriting. `dataConfig` + * adds any user-chosen data field overrides, same as fill/line. + */ +export function buildSymbolGeoJSON( + dataConfig: Record = {} +): FeatureCollection { + const features: Feature[] = []; + + for (let zoom = 0; zoom <= ZOOM_MAX; zoom += SYMBOL_ZOOM_STEP) { + features.push({ + type: 'Feature', + properties: { zoom, ...dataConfig }, + geometry: { + type: 'Point', + coordinates: [symbolZoomToLon(zoom), 0] + } + }); + } + + return { type: 'FeatureCollection', features }; +} diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/renderCache.ts b/src/lib/renderCache.ts new file mode 100644 index 0000000..526a1dc --- /dev/null +++ b/src/lib/renderCache.ts @@ -0,0 +1,53 @@ +// --------------------------------------------------------------------------- +// A module-level cache from a render job's content to its already-rendered +// data URL. Module-level state survives page navigation (SvelteKit destroys +// and recreates the fills/lines page components, and with them their +// RenderQueue and LayerBar instances) — so switching pages and back can +// reuse the last render instead of redoing the WebGL work, as long as the +// style and data config haven't changed. +// +// The cache is intentionally content-keyed (paint/layout/geojson/dimensions) +// rather than tied to a layer id — two layers that happen to render +// pixel-identically should share one entry. +// --------------------------------------------------------------------------- + +interface CacheableJob { + layerType: 'fill' | 'line' | 'symbol'; + paint: Record; + layout: Record; + geojson: unknown; + glyphs?: string; + sprite?: string; + width: number; + height: number; +} + +const cache = new Map(); + +/** Recursively sorts object keys so structurally-identical values with + * differently-ordered keys (e.g. data config fields applied in a different + * order across visits) still hash to the same string. Array order is left + * untouched — order is meaningful there (geojson features, expressions). */ +function canonicalize(value: unknown): unknown { + if (Array.isArray(value)) return value.map(canonicalize); + if (value !== null && typeof value === 'object') { + const sorted: Record = {}; + for (const key of Object.keys(value as Record).sort()) { + sorted[key] = canonicalize((value as Record)[key]); + } + return sorted; + } + return value; +} + +export function renderCacheKey(job: CacheableJob): string { + return JSON.stringify(canonicalize(job)); +} + +export function getCachedRender(key: string): string | undefined { + return cache.get(key); +} + +export function setCachedRender(key: string, dataUrl: string): void { + cache.set(key, dataUrl); +} diff --git a/src/lib/renderer.ts b/src/lib/renderer.ts new file mode 100644 index 0000000..8ab8320 --- /dev/null +++ b/src/lib/renderer.ts @@ -0,0 +1,250 @@ +import type { FeatureCollection } from 'geojson'; + +// --------------------------------------------------------------------------- +// Shared MapLibre renderer +// +// Each page (fills, lines) uses one RenderQueue — a single hidden MapLibre +// map with preserveDrawingBuffer:true. Layers are rendered sequentially: +// swap the style layer, wait for idle, capture canvas.toDataURL(), resolve. +// +// This keeps the WebGL context count to one per page regardless of how many +// layers the loaded style contains. +// +// Canvases render with a transparent background wherever nothing is drawn +// (there's no background/backdrop layer here) — the shared background swatch +// behind the whole bar list (see BackgroundSwatch.svelte) shows through those +// gaps, rather than every bar baking its own copy of it into its own capture. +// --------------------------------------------------------------------------- + +interface RenderJob { + layerId: string; + layerType: 'fill' | 'line' | 'symbol'; + paint: Record; + layout: Record; + geojson: FeatureCollection; + // Only meaningful for symbol layers — text-font/icon-image need these to + // resolve real glyphs/sprite icons. Undefined for fill/line jobs. + glyphs?: string; + sprite?: string; + width: number; + height: number; + resolve: (dataUrl: string) => void; + reject: (err: Error) => void; +} + +// "Cross-faded" paint properties (line-dasharray, *-pattern) maintain their +// own atlas/texture state behind the scenes. Toggling one of these between a +// data-driven expression and a plain/absent value on the SAME live layer +// permanently corrupts that layer's render state in MapLibre: every +// subsequent frame throws deep inside a uniform setter and 'idle' never +// fires again, hanging that render job (and every job queued behind it) +// forever. Recreating the layer (remove + add) avoids this. We don't do that +// for every job — see the comment at the mutate-in-place branch below — only +// when one of these properties is actually changing shape. +const CROSS_FADED_PAINT_PROPS = [ + 'line-dasharray', + 'line-pattern', + 'fill-pattern', + 'fill-extrusion-pattern' +]; + +function isExpressionValue(value: unknown): boolean { + return Array.isArray(value) && typeof value[0] === 'string'; +} + +export class RenderQueue { + private queue: RenderJob[] = []; + private busy = false; + private map: import('maplibre-gl').Map | null = null; + private container: HTMLDivElement | null = null; + private mapWidth = 0; + private mapHeight = 0; + private prevPaint: Record = {}; + private prevLayout: Record = {}; + private prevGlyphs: string | undefined; + private prevSprite: string | undefined; + + enqueue(job: Omit): Promise { + return new Promise((resolve, reject) => { + this.queue.push({ ...job, resolve, reject }); + if (!this.busy) this.drain(); + }); + } + + private async drain() { + this.busy = true; + while (this.queue.length > 0) { + const job = this.queue.shift()!; + try { + const url = await this.renderOne(job); + job.resolve(url); + } catch (err) { + job.reject(err instanceof Error ? err : new Error(String(err))); + } + } + this.busy = false; + } + + private async renderOne(job: RenderJob): Promise { + const maplibre = await import('maplibre-gl'); + + const needsResize = job.width !== this.mapWidth || job.height !== this.mapHeight; + + if (!this.map || !this.container) { + // First use — create the hidden off-screen container and map. + this.container = document.createElement('div'); + Object.assign(this.container.style, { + position: 'fixed', + left: '-99999px', + top: '0', + width: `${job.width}px`, + height: `${job.height}px`, + visibility: 'hidden' + }); + document.body.appendChild(this.container); + this.mapWidth = job.width; + this.mapHeight = job.height; + + const fitZoom = Math.log2(job.width / 512); + + this.map = new maplibre.Map({ + container: this.container, + style: { + version: 8, + // MapLibre's style validation rejects an explicit + // `undefined` value for these — they must be either a + // real string or the key absent entirely (fill/line jobs + // never set them). + ...(job.glyphs !== undefined ? { glyphs: job.glyphs } : {}), + ...(job.sprite !== undefined ? { sprite: job.sprite } : {}), + sources: { + 'zoom-segments': { type: 'geojson', data: job.geojson } + }, + layers: [ + { + id: '__layer__', + type: job.layerType, + source: 'zoom-segments', + layout: job.layout as never, + paint: job.paint as never + } + ] + }, + center: [0, 0], + zoom: fitZoom, + renderWorldCopies: false, + interactive: false, + attributionControl: false, + // preserveDrawingBuffer is required for toDataURL() after the frame. + // In MapLibre v5 this moved under canvasContextAttributes. + canvasContextAttributes: { preserveDrawingBuffer: true } + }); + + await new Promise((res, rej) => { + this.map!.once('load', () => res()); + this.map!.once('error', (e) => rej(e.error ?? new Error('map load error'))); + }); + } else { + // Subsequent use — update the existing map in place. + if (needsResize) { + this.container.style.width = `${job.width}px`; + this.container.style.height = `${job.height}px`; + this.mapWidth = job.width; + this.mapHeight = job.height; + this.map.resize(); + } + + // Update the GeoJSON source data (same for all layers of a type, + // but we pass it through to keep the API simple). + (this.map.getSource('zoom-segments') as import('maplibre-gl').GeoJSONSource).setData( + job.geojson + ); + + // Only relevant for symbol layers, and only actually changes when + // a different style is loaded on the same page (glyphs/sprite are + // shared across every layer on a page, unlike per-layer paint). + if (job.glyphs !== this.prevGlyphs) this.map.setGlyphs(job.glyphs ?? null); + if (job.sprite !== this.prevSprite) this.map.setSprite(job.sprite ?? null); + + const needsRecreate = CROSS_FADED_PAINT_PROPS.some( + (key) => isExpressionValue(this.prevPaint[key]) !== isExpressionValue(job.paint[key]) + ); + + if (needsRecreate) { + // Let the setData() mutation above fully settle before tearing + // down the layer — recreating it while a source reload is + // still in flight is what causes a rare, non-fatal, + // self-recovering console error here. + await this.waitIdle(); + + // See CROSS_FADED_PAINT_PROPS above — mutating in place here + // permanently corrupts this layer's render state in MapLibre. + this.map.removeLayer('__layer__'); + this.map.addLayer({ + id: '__layer__', + type: job.layerType, + source: 'zoom-segments', + layout: job.layout, + paint: job.paint + } as Parameters[0]); + } else { + // Mutate the existing layer's paint/layout in place rather + // than remove+add — removing the layer destroys its bucket + // mid-frame, and a render already queued by setData()/ + // setPaintProperty() above can fire against the half-torn- + // down layer, throwing deep inside MapLibre's uniform + // setters (undefined color arrays). + const paintKeys = new Set(Object.keys(job.paint)); + for (const key of Object.keys(this.prevPaint)) { + if (!paintKeys.has(key)) this.map.setPaintProperty('__layer__', key, undefined); + } + for (const [key, value] of Object.entries(job.paint)) { + this.map.setPaintProperty('__layer__', key, value); + } + + const layoutKeys = new Set(Object.keys(job.layout)); + for (const key of Object.keys(this.prevLayout)) { + if (!layoutKeys.has(key)) this.map.setLayoutProperty('__layer__', key, undefined); + } + for (const [key, value] of Object.entries(job.layout)) { + this.map.setLayoutProperty('__layer__', key, value); + } + } + + // Fit zoom in case width changed. + if (needsResize) { + const fitZoom = Math.log2(job.width / 512); + this.map.setZoom(fitZoom); + } + } + + this.prevPaint = job.paint; + this.prevLayout = job.layout; + this.prevGlyphs = job.glyphs; + this.prevSprite = job.sprite; + + await this.waitIdle(); + + return this.map!.getCanvas().toDataURL(); + } + + private waitIdle(): Promise { + return new Promise((res, rej) => { + const onError = (e: { error?: Error }) => rej(e.error ?? new Error('render error')); + this.map!.once('idle', () => { + this.map!.off('error', onError); + res(); + }); + this.map!.once('error', onError); + }); + } + + destroy() { + this.map?.remove(); + this.container?.remove(); + this.map = null; + this.container = null; + this.mapWidth = 0; + this.mapHeight = 0; + } +} diff --git a/src/lib/styleParser.ts b/src/lib/styleParser.ts new file mode 100644 index 0000000..e62818e --- /dev/null +++ b/src/lib/styleParser.ts @@ -0,0 +1,155 @@ +import type { + StyleSpecification, + FillLayerSpecification, + LineLayerSpecification +} from 'maplibre-gl'; +import { rewritePaint, rewriteTextField } from './expressionRewriter'; + +// --------------------------------------------------------------------------- +// Style parser +// +// Extracts fill and line layers from a loaded Mapbox/Maplibre style and +// transforms them for our zoom-level visualization. +// +// Transformations applied to every layer: +// +// 1. Strip `source` and `source-layer` — we substitute our own fake +// GeoJSON source in LayerBar; the original tile source is irrelevant. +// +// 2. Strip `filter` — filters reference feature properties (like "class") +// that don't exist on our fake features, so they'd hide everything. +// +// 3. Strip `minzoom` / `maxzoom` — we want to see the full paint expression +// across the 0–22 range regardless of the layer's visibility limits. +// +// 4. Rewrite zoom expressions in `paint` — replaces ["zoom"] with +// ["get", "zoom"] so the renderer reads the feature's "zoom" property +// instead of the camera zoom (see expressionRewriter.ts). +// +// 5. Separate layout properties — line-cap, line-join, etc. are LAYOUT +// properties in MapLibre, not paint properties. Putting them in the +// wrong block causes MapLibre to silently ignore them. +// --------------------------------------------------------------------------- + +export interface VisFillLayer { + id: string; + paint: Record; + layout: Record; + // Present when the layer has fill-outline-color. Remapped as fill-color + // so a thin overlay bar can display it without polluting the main bar's + // polygon edge rendering. + outlinePaint: Record | null; +} + +export interface VisLineLayer { + id: string; + paint: Record; + layout: Record; +} + +export interface VisSymbolLayer { + id: string; + paint: Record; + layout: Record; +} + +export function extractFillLayers(style: StyleSpecification): VisFillLayer[] { + return style.layers + .filter((l): l is FillLayerSpecification => l.type === 'fill') + .map((layer) => { + const paint = rewritePaint((layer.paint ?? {}) as Record); + + // Pull fill-outline-color out of the main paint before rendering. + // Left in place, MapLibre draws it on every polygon edge — with 220 + // strips side by side that creates 220 visible seam lines. Instead we + // remap it as fill-color in a separate thin overlay bar so it's shown + // once, cleanly, at the bottom of the fill bar. + const outlineColorExpr = paint['fill-outline-color'] ?? null; + delete paint['fill-outline-color']; + + return { + id: layer.id, + paint, + layout: {}, + outlinePaint: outlineColorExpr ? { 'fill-color': outlineColorExpr } : null + }; + }); +} + +export function extractLineLayers(style: StyleSpecification): VisLineLayer[] { + return style.layers + .filter((l): l is LineLayerSpecification => l.type === 'line') + .map((layer) => ({ + id: layer.id, + paint: rewritePaint((layer.paint ?? {}) as Record), + // line-cap and line-join are LAYOUT properties (not paint). + // "butt" caps prevent adjacent segments from visually overlapping + // at high line-width values. "miter" join keeps segments flush. + layout: { + 'line-cap': 'butt', + 'line-join': 'miter' + } + })); +} + +export function extractSymbolLayers(style: StyleSpecification): VisSymbolLayer[] { + return style.layers + .filter((l) => l.type === 'symbol') + .map((rawLayer) => { + const layer = rawLayer as unknown as { + id: string; + paint?: Record; + layout?: Record; + }; + const paint = rewritePaint(layer.paint ?? {}); + const layout = rewritePaint(layer.layout ?? {}); + + if ('text-field' in layout) { + layout['text-field'] = rewriteTextField(layout['text-field'] as never) as never; + } + + // Force every sample to render regardless of the style's own + // collision settings — without this, MapLibre's collision + // detection can hide some of our zoom samples as "overlapping" + // with their neighbors, which would look like a rendering bug. + // Points can't be placed "along a line" either, so pin placement + // to plain points regardless of what the style specifies. + layout['text-allow-overlap'] = true; + layout['icon-allow-overlap'] = true; + layout['text-ignore-placement'] = true; + layout['icon-ignore-placement'] = true; + layout['symbol-placement'] = 'point'; + + return { id: layer.id, paint, layout }; + }); +} + +/** + * Extracts the style's background layer paint (if any), rewritten the same + * way as every other layer, and remapped to fill-color/fill-opacity so it + * can be drawn as a real fill layer over real geometry — see renderer.ts for + * why that's necessary (MapLibre's background layer type is sourceless, so + * it can't vary across our synthetic zoom segments the way every other + * layer does). background-pattern is ignored — patterns need a sprite atlas + * we don't load. Falls back to `fallbackColor` when the style has no + * background layer, or the layer doesn't set a color. + */ +export function extractBackgroundPaint( + style: StyleSpecification, + fallbackColor: string +): Record { + const bgLayer = style.layers.find((l) => l.type === 'background') as + | { paint?: Record } + | undefined; + + if (!bgLayer?.paint) return { 'fill-color': fallbackColor }; + + const rewritten = rewritePaint(bgLayer.paint); + const paint: Record = { + 'fill-color': rewritten['background-color'] ?? fallbackColor + }; + if ('background-opacity' in rewritten) { + paint['fill-opacity'] = rewritten['background-opacity']; + } + return paint; +} diff --git a/src/lib/styleStore.svelte.ts b/src/lib/styleStore.svelte.ts new file mode 100644 index 0000000..def5782 --- /dev/null +++ b/src/lib/styleStore.svelte.ts @@ -0,0 +1,63 @@ +import { migrate } from '@maplibre/maplibre-gl-style-spec'; +import type { StyleSpecification } from 'maplibre-gl'; +import { collectDataFields, type DataFieldsByType, type DataFieldValue } from './dataFieldCollector'; + +/** User-chosen override values, grouped by layer type then field name. A + * field absent from a type's map means "use the fallback" (no override). */ +export type DataConfigByType = Record>; + +// --------------------------------------------------------------------------- +// Global reactive state for the currently loaded Mapbox / Maplibre style. +// +// Svelte 5 rune-based state in a .svelte.ts module is shared across all +// components that import it — mutations trigger reactive updates everywhere. +// --------------------------------------------------------------------------- + +class StyleStore { + /** The parsed style object, or null when nothing has been loaded yet. */ + current = $state(null); + + /** Every match/case-branch value the style references, grouped by layer type. */ + dataFields = $state({}); + + /** The user's currently selected override per layer type/field. */ + dataConfig = $state({}); + + /** Set (or clear, when value is null) the override for one field on one layer type. */ + setDataConfigValue(layerType: string, field: string, value: DataFieldValue | null) { + if (value === null) { + delete this.dataConfig[layerType]?.[field]; + return; + } + (this.dataConfig[layerType] ??= {})[field] = value; + } + + /** Parse and store a style from a raw JSON string (e.g. from a dropped file). */ + loadFromJSON(json: string) { + const raw = JSON.parse(json) as StyleSpecification; + + // Run the style through the MapLibre migrator before storing it. + // Many real-world styles (especially older Mapbox ones) still use the + // legacy "function" syntax — { base, stops } objects — instead of the + // modern expression syntax. Our expression rewriter only understands + // arrays, so it silently passes legacy functions through without + // rewriting the zoom references. + // + // migrate() converts every { base, stops } function to the equivalent + // ["interpolate", ["zoom"], ...] expression, giving our rewriter a + // consistent tree to walk regardless of how old the source style is. + const migrated = migrate(raw) as StyleSpecification; + this.current = migrated; + this.dataFields = collectDataFields(migrated); + this.dataConfig = {}; + } + + /** Discard the currently loaded style and return to the drop-zone screen. */ + clear() { + this.current = null; + this.dataFields = {}; + this.dataConfig = {}; + } +} + +export const styleStore = new StyleStore(); diff --git a/src/main.js b/src/main.js deleted file mode 100644 index 375a2fe..0000000 --- a/src/main.js +++ /dev/null @@ -1,10 +0,0 @@ -import App from './App.svelte'; - -const app = new App({ - target: document.body, - props: { - loadDefaultStyle: process.env.NODE_ENV === 'development', - }, -}); - -export default app; diff --git a/src/query.js b/src/query.js deleted file mode 100644 index 88c8a4c..0000000 --- a/src/query.js +++ /dev/null @@ -1,16 +0,0 @@ -export function readQuery() { - return Object.fromEntries( - window.location.hash - .slice(1) - .split('&') - .map(s => s.split('=')) - ); -} - -export function writeQuery(state) { - window.location.hash = Object.entries(state) - .map(([k, v]) => { - return [encodeURIComponent(k), encodeURIComponent(v)].join('='); - }) - .join('&'); -} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..326c570 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,109 @@ + + + + Chartographer + + +{#if hasStyle} + +
+ + +
+ {@render children()} +
+
+{:else} + + +{/if} + + diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..f9304f3 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,4 @@ +// Every route is client-driven (style upload, rendering) with no server-side +// data, so the whole app can be prerendered into a static site for GitHub +// Pages — see vite.config.ts's adapter-static config. +export const prerender = true; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..80d76ae --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,10 @@ + diff --git a/src/routes/fills/+page.svelte b/src/routes/fills/+page.svelte new file mode 100644 index 0000000..43af5c0 --- /dev/null +++ b/src/routes/fills/+page.svelte @@ -0,0 +1,158 @@ + + + + +{#if fillLayers.length === 0} +

No fill layers found in this style.

+{:else} + + +
+ {#each zoomTicks as z} + {z} + {/each} +
+ + + + {#each fillLayers as layer (layer.id)} +
+ + {#if layer.outlinePaint} + + {/if} +
+ {/each} +
+{/if} + + diff --git a/src/routes/lines/+page.svelte b/src/routes/lines/+page.svelte new file mode 100644 index 0000000..1979c1b --- /dev/null +++ b/src/routes/lines/+page.svelte @@ -0,0 +1,134 @@ + + + + +{#if lineLayers.length === 0} +

No line layers found in this style.

+{:else} +
+ {#each zoomTicks as z} + {z} + {/each} +
+ + + + {#each lineLayers as layer (layer.id)} + + {/each} + +{/if} + + diff --git a/src/routes/symbols/+page.svelte b/src/routes/symbols/+page.svelte new file mode 100644 index 0000000..a1b35f1 --- /dev/null +++ b/src/routes/symbols/+page.svelte @@ -0,0 +1,181 @@ + + + + +{#if symbolLayers.length === 0} +

No symbol layers found in this style.

+{:else} + +
+ +
+ {#each zoomTicks as z} + {z} + {/each} +
+ + + {#each symbolLayers as layer (layer.id)} + + {/each} + +
+{/if} + + diff --git a/src/shortcut.js b/src/shortcut.js deleted file mode 100644 index 90240ad..0000000 --- a/src/shortcut.js +++ /dev/null @@ -1,26 +0,0 @@ -// See https://svelte.dev/repl/acd92c9726634ec7b3d8f5f759824d15?version=3.46.4 for more info -export const shortcut = (node, params) => { - let handler; - const removeHandler = () => window.removeEventListener('keydown', handler), - setHandler = () => { - removeHandler(); - if (!params) return; - handler = e => { - if ( - !!params.alt != e.altKey || - !!params.shift != e.shiftKey || - !!params.control != (e.ctrlKey || e.metaKey) || - params.code != e.code - ) - return; - e.preventDefault(); - params.callback ? params.callback() : node.click(); - }; - window.addEventListener('keydown', handler); - }; - setHandler(); - return { - update: setHandler, - destroy: removeHandler, - }; -}; diff --git a/src/stores.js b/src/stores.js deleted file mode 100644 index 69e95dd..0000000 --- a/src/stores.js +++ /dev/null @@ -1,38 +0,0 @@ -import { writable } from 'svelte/store'; - -export const styleStore = writable({ style: null, url: null }); - -export const propertyValueComboLimitStore = writable(10); - -export const loadingStore = writable({ loading: false, progress: null }); - -export const displayLayersStoreInitialState = { - style: null, - layers: [], - limitHit: [], -}; -export const displayLayersStore = writable(displayLayersStoreInitialState); - -export const svgStoreInitialState = { - fills: { - background: null, - svgs: [], - gradients: [], - chartHeight: null, - yScale: null, - adjustedYScale: null, - }, - lines: { - background: null, - svgs: [], - gradients: [], - chartHeight: null, - yScale: null, - adjustedYScale: null, - }, -}; -export const svgStore = writable(svgStoreInitialState); - -export let rendererStore = writable('mapbox-gl'); - -export const mapboxGlAccessTokenStore = writable(''); diff --git a/src/styles/__test__/expandLayers.test.js b/src/styles/__test__/expandLayers.test.js deleted file mode 100644 index 16d19d7..0000000 --- a/src/styles/__test__/expandLayers.test.js +++ /dev/null @@ -1,856 +0,0 @@ -import { - getPropertyValues, - parseConditionalExpression, - parseScaleExpression, - expandLayer, -} from '../expandLayers'; - -describe('getPropertyValues', () => { - let expression; - test('gets property values for match', () => { - expression = ['match', ['get', 'class'], 'grass', 'green', 'red']; - const actual = getPropertyValues(expression); - const expected = { - propertyValues: { class: ['grass', 'FALLBACK'] }, - zooms: [], - }; - expect(actual).toEqual(expected); - }); - - test('gets property values for scale', () => { - expression = [ - 'interpolate', - ['linear'], - ['zoom'], - 3, - ['match', ['get', 'class'], 'grass', 'green', 'red'], - 5, - ['match', ['get', 'type'], ['water', 'landmark'], 'blue', 'purple'], - ]; - const actual = getPropertyValues(expression); - const expected = { - propertyValues: { - class: ['grass', 'FALLBACK'], - type: ['water', 'landmark', 'FALLBACK'], - }, - zooms: [3, 5], - }; - expect(actual).toEqual(expected); - }); -}); - -describe('parseScaleExpression', () => { - let expression; - - test('parse scale expression', () => { - expression = [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - ['match', ['get', 'class'], 'grass', 'green', 'purple'], - 10, - [ - 'case', - ['==', ['get', 'class'], 'grass'], - 'green', - ['==', ['get', 'class'], 'water'], - 'blue', - ['==', ['get', 'type'], 'fire'], - 'red', - 'black', - ], - ]; - const actual = parseScaleExpression(expression); - const expected = { - zooms: [5, 10], - outputs: ['green', 'purple', 'blue', 'red', 'black'], - properties: { class: ['grass', 'grass', 'water'], type: ['fire'] }, - }; - expect(actual).toEqual(expected); - }); -}); - -describe('parseConditionalExpression', () => { - let expression; - - test('parse match expression', () => { - expression = ['match', ['get', 'class'], 'grass', 'green', 'red']; - const actual = parseConditionalExpression(expression); - const expected = { - inputs: ['grass'], - outputs: ['green', 'red'], - properties: { class: ['grass'] }, - }; - expect(actual).toEqual(expected); - }); - - test('parse case expression', () => { - expression = [ - 'case', - ['==', ['get', 'class'], 'grass'], - 'green', - ['==', ['get', 'class'], 'water'], - 'blue', - 'red', - ]; - const actual = parseConditionalExpression(expression); - const expected = { - inputs: [ - ['==', ['get', 'class'], 'grass'], - ['==', ['get', 'class'], 'water'], - ['==', ['get', 'class'], 'grass'], - ['==', ['get', 'class'], 'water'], - ], - outputs: ['green', 'blue', 'red'], - properties: { class: ['grass', 'water'] }, - }; - expect(actual).toEqual(expected); - }); - - test('parse nested match expression', () => { - expression = [ - 'match', - ['get', 'class'], - 'grass', - ['match', ['get', 'type'], 'spring', 'green', 'brown'], - 'red', - ]; - const actual = parseConditionalExpression(expression); - const expected = { - inputs: ['grass', 'spring'], - outputs: ['green', 'brown', 'red'], - properties: { class: ['grass'], type: ['spring'] }, - }; - expect(actual).toEqual(expected); - }); -}); - -describe('expandLayer', () => { - let layer; - test('expands layer', () => { - layer = { - id: 'test-layer', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - [ - 'match', - ['get', 'class'], - ['blue', 'near-blue'], - 'blue', - ['match', ['get', 'class'], 'green', 'green', 'black'], - ], - 10, - 'red', - ], - 'fill-opacity': [ - 'step', - ['zoom'], - 0.5, - 2, - ['match', ['get', 'type'], 'clear', 0.2, 1], - 7, - 1, - ], - 'fill-antialias': false, - }, - }; - const { expandedLayers, comboLimitHit } = expandLayer(layer); - const expectedLayers = [ - { - id: 'test-layer/class: "blue"/type: "clear"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 0, 255, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 0, 255, 1)', - 2.1, - 'rgba(0, 0, 255, 1)', - 7, - 'rgba(102, 0, 153, 1)', - 7.1, - 'rgba(107, 0, 148, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 0.2, - 10, - 1, - 2, - 0.2, - 2.1, - 0.2, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "blue"/type: "FALLBACK"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 0, 255, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 0, 255, 1)', - 2.1, - 'rgba(0, 0, 255, 1)', - 7, - 'rgba(102, 0, 153, 1)', - 7.1, - 'rgba(107, 0, 148, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 1, - 10, - 1, - 2, - 1, - 2.1, - 1, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "near-blue"/type: "clear"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 0, 255, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 0, 255, 1)', - 2.1, - 'rgba(0, 0, 255, 1)', - 7, - 'rgba(102, 0, 153, 1)', - 7.1, - 'rgba(107, 0, 148, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 0.2, - 10, - 1, - 2, - 0.2, - 2.1, - 0.2, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "near-blue"/type: "FALLBACK"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 0, 255, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 0, 255, 1)', - 2.1, - 'rgba(0, 0, 255, 1)', - 7, - 'rgba(102, 0, 153, 1)', - 7.1, - 'rgba(107, 0, 148, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 1, - 10, - 1, - 2, - 1, - 2.1, - 1, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "green"/type: "clear"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 128, 0, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 128, 0, 1)', - 2.1, - 'rgba(0, 128, 0, 1)', - 7, - 'rgba(102, 77, 0, 1)', - 7.1, - 'rgba(107, 74, 0, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 0.2, - 10, - 1, - 2, - 0.2, - 2.1, - 0.2, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "green"/type: "FALLBACK"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 128, 0, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 128, 0, 1)', - 2.1, - 'rgba(0, 128, 0, 1)', - 7, - 'rgba(102, 77, 0, 1)', - 7.1, - 'rgba(107, 74, 0, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 1, - 10, - 1, - 2, - 1, - 2.1, - 1, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "FALLBACK"/type: "clear"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 0, 0, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 0, 0, 1)', - 2.1, - 'rgba(0, 0, 0, 1)', - 7, - 'rgba(102, 0, 0, 1)', - 7.1, - 'rgba(107, 0, 0, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 0.2, - 10, - 1, - 2, - 0.2, - 2.1, - 0.2, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - { - id: 'test-layer/class: "FALLBACK"/type: "FALLBACK"', - type: 'fill', - metadata: {}, - source: 'composite', - 'source-layer': 'landcover', - layout: {}, - paint: { - 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 'rgba(0, 0, 0, 1)', - 10, - 'rgba(255, 0, 0, 1)', - 2, - 'rgba(0, 0, 0, 1)', - 2.1, - 'rgba(0, 0, 0, 1)', - 7, - 'rgba(102, 0, 0, 1)', - 7.1, - 'rgba(107, 0, 0, 1)', - ], - 'fill-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 5, - 1, - 10, - 1, - 2, - 1, - 2.1, - 1, - 7, - 1, - 7.1, - 1, - ], - 'fill-antialias': false, - }, - }, - ]; - expect(expandedLayers).toEqual(expectedLayers); - expect(comboLimitHit).toBe(false); - }); - - test('expands layer with lots of properties with hard limit (10)', () => { - layer = { - id: 'test-layer', - type: 'line', - source: 'openmaptiles', - 'source-layer': 'transportation', - minzoom: 4, - filter: [ - 'all', - [ - 'in', - ['get', 'class'], - [ - 'literal', - [ - 'motorway', - 'trunk', - 'primary', - 'secondary', - 'tertiary', - 'minor', - 'service', - ], - ], - ], - ['==', ['get', 'brunnel'], 'tunnel'], - ], - layout: { - 'line-cap': 'round', - 'line-join': 'round', - 'line-sort-key': [ - '+', - ['*', -28, ['coalesce', ['get', 'ramp'], 0]], - [ - '*', - 4, - [ - 'match', - ['get', 'class'], - 'motorway', - 6, - 'trunk', - 5, - 'primary', - 4, - 'secondary', - 3, - 'tertiary', - 2, - 'minor', - 1, - 0, - ], - ], - ['*', 2, ['coalesce', ['get', 'expressway'], 0]], - ['coalesce', ['get', 'toll'], 0], - ], - visibility: 'visible', - }, - paint: { - 'line-opacity': [ - 'step', - ['zoom'], - [ - 'match', - ['coalesce', ['get', 'ramp'], 0], - 1, - 0, - ['match', ['get', 'network'], 'us-interstate', 1, 0], - ], - 5, - [ - 'match', - ['coalesce', ['get', 'ramp'], 0], - 1, - 0, - ['match', ['get', 'class'], ['motorway', 'trunk'], 1, 0], - ], - 7, - ['match', ['get', 'class'], ['motorway', 'trunk', 'primary'], 1, 0], - 9, - [ - 'match', - ['get', 'class'], - ['motorway', 'trunk', 'primary', 'secondary'], - 1, - 0, - ], - 11, - [ - 'match', - ['get', 'class'], - ['motorway', 'trunk', 'primary', 'secondary', 'tertiary'], - 1, - 0, - ], - 12, - ['match', ['get', 'class'], 'service', 0, 1], - 13, - [ - 'match', - ['get', 'class'], - 'service', - ['match', ['get', 'service'], ['parking_aisle', 'driveway'], 0, 1], - 1, - ], - 15, - 1, - ], - 'line-color': [ - 'match', - ['get', 'brunnel'], - 'tunnel', - [ - 'match', - ['get', 'class'], - 'motorway', - [ - 'match', - ['coalesce', ['get', 'toll'], 0], - 1, - 'hsl(48, 71%, 90%)', - 'hsl(0, 71%, 90%)', - ], - 'trunk', - [ - 'match', - ['coalesce', ['get', 'toll'], 0], - 1, - 'hsl(48, 77%, 90%)', - 'hsl(0, 77%, 90%)', - ], - [ - 'match', - ['coalesce', ['get', 'toll'], 0], - 1, - 'hsl(48, 100%, 95%)', - 'hsl(0, 0%, 95%)', - ], - ], - [ - 'match', - ['get', 'class'], - 'trunk', - [ - 'match', - ['coalesce', ['get', 'expressway'], 0], - 1, - [ - 'match', - ['coalesce', ['get', 'toll'], 0], - 1, - 'hsl(48, 95%, 95%)', - 'hsl(0, 95%, 95%)', - ], - [ - 'match', - ['coalesce', ['get', 'toll'], 0], - 1, - 'hsl(48, 77%, 50%)', - 'hsl(0, 77%, 50%)', - ], - ], - [ - 'match', - ['coalesce', ['get', 'toll'], 0], - 1, - 'hsl(48, 100%, 75%)', - 'hsl(0, 100%, 100%)', - ], - ], - ], - 'line-width': [ - 'interpolate', - ['exponential', 1.2], - ['zoom'], - 4, - [ - '*', - 0.5, - [ - 'match', - ['get', 'class'], - ['motorway', 'trunk'], - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.5, 1], - 'primary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.45, 0.9], - 'secondary', - [ - 'match', - ['coalesce', ['get', 'ramp'], 0], - 1, - 0.3, - ['match', ['coalesce', ['get', 'expressway'], 0], 1, 0.7, 0.6], - ], - 'tertiary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.25, 0.5], - 'minor', - 0.3, - 'service', - [ - 'match', - ['get', 'service'], - ['parking_aisle', 'driveway'], - 0.15, - 0.2, - ], - 0.2, - ], - ], - 9, - [ - 'match', - ['get', 'class'], - ['motorway', 'trunk'], - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.5, 1], - 'primary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.45, 0.9], - 'secondary', - [ - 'match', - ['coalesce', ['get', 'ramp'], 0], - 1, - 0.3, - ['match', ['coalesce', ['get', 'expressway'], 0], 1, 0.7, 0.6], - ], - 'tertiary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.25, 0.5], - 'minor', - 0.3, - 'service', - [ - 'match', - ['get', 'service'], - ['parking_aisle', 'driveway'], - 0.15, - 0.2, - ], - 0.2, - ], - 12, - [ - '*', - [ - 'match', - ['get', 'class'], - 'motorway', - 3.2, - ['match', ['coalesce', ['get', 'expressway'], 0], 1, 3.5, 4], - ], - [ - 'match', - ['get', 'class'], - ['motorway', 'trunk'], - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.5, 1], - 'primary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.45, 0.9], - 'secondary', - [ - 'match', - ['coalesce', ['get', 'ramp'], 0], - 1, - 0.3, - ['match', ['coalesce', ['get', 'expressway'], 0], 1, 0.7, 0.6], - ], - 'tertiary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.25, 0.5], - 'minor', - 0.3, - 'service', - [ - 'match', - ['get', 'service'], - ['parking_aisle', 'driveway'], - 0.15, - 0.2, - ], - 0.2, - ], - ], - 20, - [ - '*', - ['match', ['coalesce', ['get', 'expressway'], 0], 1, 16, 18], - [ - 'match', - ['get', 'class'], - ['motorway', 'trunk'], - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.5, 1], - 'primary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.45, 0.9], - 'secondary', - [ - 'match', - ['coalesce', ['get', 'ramp'], 0], - 1, - 0.3, - ['match', ['coalesce', ['get', 'expressway'], 0], 1, 0.7, 0.6], - ], - 'tertiary', - ['match', ['coalesce', ['get', 'ramp'], 0], 1, 0.25, 0.5], - 'minor', - 0.3, - 'service', - [ - 'match', - ['get', 'service'], - ['parking_aisle', 'driveway'], - 0.15, - 0.2, - ], - 0.2, - ], - ], - ], - 'line-blur': 0.5, - }, - }; - const { expandedLayers, comboLimitHit } = expandLayer(layer); - expect(expandedLayers.length).toEqual(10); - expect(comboLimitHit).toBe(true); - }); -}); diff --git a/src/styles/evaluate-expression.js b/src/styles/evaluate-expression.js deleted file mode 100644 index 1142716..0000000 --- a/src/styles/evaluate-expression.js +++ /dev/null @@ -1,86 +0,0 @@ -import Color from 'color'; -import { latest } from '@mapbox/mapbox-gl-style-spec'; -import { Expression } from 'mapbox-expression'; - -// From https://github.com/mapbox/mapbox-gl-js/tree/c3910f870db46aba2a50db3111158244126cb19a/src/style-spec/expression/definitions -const serializeFormatted = formatted => { - const { sections } = formatted; - const serialized = ['format']; - for (const section of sections) { - if (section.image) { - serialized.push(['image', section.image.name]); - continue; - } - serialized.push(section.text); - const options = {}; - if (section.fontStack) { - options['text-font'] = ['literal', section.fontStack.split(',')]; - } - if (section.scale) { - options['font-scale'] = section.scale; - } - if (section.textColor) { - options['text-color'] = ['rgba'].concat(section.textColor.toArray()); - } - serialized.push(options); - } - return serialized; -}; - -// From https://github.com/mapbox/mapbox-gl-js/tree/c3910f870db46aba2a50db3111158244126cb19a/src/style-spec/expression/definitions -const serializeIconImage = resolvedImage => { - return ['image', resolvedImage.name]; -}; - -const evaluateExpression = ({ - layerType, - propertyType, - propertyId, - properties, - value, - zoom, -}) => { - const initialKey = `${propertyType}_${layerType}`; - const propertySpec = latest[initialKey][propertyId]; - const feature = { - type: 'Feature', - properties, - geometry: null, - }; - - let nextValue = Expression.parse(value, propertySpec.type).evaluate( - feature, - zoom && { - zoom, - } - ); - - if ( - propertySpec.type === 'color' && - nextValue && - nextValue.hasOwnProperty('r') - ) { - nextValue = `rgba(${Math.round(255 * nextValue.r)}, ${Math.round( - 255 * nextValue.g - )}, ${Math.round(255 * nextValue.b)}, ${nextValue.a})`; - } - - if ( - propertyId === 'icon-image' && - nextValue && - nextValue.hasOwnProperty('name') - ) { - nextValue = serializeIconImage(nextValue); - } - if ( - propertyId === 'text-field' && - nextValue && - nextValue.hasOwnProperty('sections') - ) { - nextValue = serializeFormatted(nextValue); - } - - return nextValue; -}; - -export { evaluateExpression }; diff --git a/src/styles/expandLayers.js b/src/styles/expandLayers.js deleted file mode 100644 index c55a440..0000000 --- a/src/styles/expandLayers.js +++ /dev/null @@ -1,422 +0,0 @@ -import cartesian from 'cartesian'; -import mergeWith from 'lodash.mergewith'; -import { propertyValueComboLimitStore } from '../stores'; -import { expression, latest } from '@mapbox/mapbox-gl-style-spec'; -const { isExpression } = expression; -import { evaluateExpression } from './evaluate-expression'; - -let comboValueLimit; -propertyValueComboLimitStore.subscribe(value => { - comboValueLimit = value; -}); - -const FALLBACKS = { - string: 'FALLBACK', - number: -1000, - array: [], - object: {}, - boolean: false, -}; - -const mergeWithCustomizer = (objValue, srcValue) => { - if (Array.isArray(objValue)) { - return objValue.concat(srcValue); - } -}; - -// Math operators from the style spec -const mathOperators = Object.entries( - latest?.expression_name?.values ?? {} -)?.reduce((acc, kv) => { - const [k, v] = kv; - if (v?.group === 'Math') { - acc.push(k); - } - return acc; -}, []); - -export const isHandledConditional = value => { - if (!Array.isArray(value)) return false; - if (mathOperators.includes(value[0])) { - return value.some(v => isHandledConditional(v)); - } - return value[0] === 'match' || value[0] === 'case'; -}; - -export const isHandledScale = value => { - const isInterpolate = - value[0] === 'interpolate' && value?.[2]?.[0] === 'zoom'; - const isStep = value[0] === 'step' && value?.[1]?.[0] === 'zoom'; - const isScale = isInterpolate || isStep; - return isScale && value.some(v => isHandledConditional(v)); -}; - -// Parses a scale expression and returns all properties in nested conditionals along with relevant zooms -export const parseScaleExpression = value => { - const [scaleType] = value; - let inputOutputs = []; - let zooms = []; - let scaleOutputs = []; - let outputs = []; - let properties = {}; - - // Pull out outputs and zooms from interpolation - switch (scaleType) { - case 'interpolate': - case 'interpolate-hcl': - case 'interpolate-lab': { - inputOutputs = value.slice(3); - inputOutputs.forEach((val, i) => - i % 2 !== 0 ? scaleOutputs.push(val) : zooms.push(val) - ); - break; - } - case 'step': { - inputOutputs = value.slice(2); - inputOutputs.forEach((val, i) => - i % 2 === 0 ? scaleOutputs.push(val) : zooms.push(val) - ); - break; - } - } - for (const scaleOutput of scaleOutputs) { - if (isHandledConditional(scaleOutput)) { - const { outputs: scaleOutputs, properties: scaleProperties } = - parseConditionalExpression(scaleOutput); - outputs = [...new Set(outputs.concat(scaleOutputs))]; - properties = mergeWith(properties, scaleProperties, mergeWithCustomizer); - } - } - return { zooms, outputs, properties }; -}; - -// TODO this needs to be a little more advanced -// assumes one property for now -const parseCaseCondition = value => { - const flatValue = value.flat(Infinity); - const lookups = ['get', 'has', 'in']; - const properties = flatValue.filter( - (item, i) => i !== 0 && lookups.includes(flatValue[i - 1]) - ); - // Just first one for now assuming 1 - const property = properties[0]; - let inputs = flatValue.filter( - item => !properties.includes(item) && !isExpression([item]) - ); - // This is a hack for ref length we should handle better - if (flatValue.includes('length') && properties.length === 1) { - inputs = inputs.map(num => `${num}`.repeat(num)); - } - return { [property]: inputs }; -}; - -// Returns properties and outputs of conditional expression -export const parseConditionalExpression = value => { - const expressionType = value[0]; - let inputOutputs = []; - let inputs = []; - let outputs = []; - let properties = {}; - - switch (expressionType) { - case 'case': { - inputOutputs = value.slice(1); - let caseInputs = []; - const fallback = inputOutputs.pop(); - inputOutputs.forEach((val, i) => - i % 2 !== 0 ? outputs.push(val) : caseInputs.push(val) - ); - outputs.push(fallback); - - caseInputs.forEach(input => { - const caseProperties = parseCaseCondition(input); - inputs = inputs.concat(caseInputs); - properties = mergeWith(properties, caseProperties, mergeWithCustomizer); - }); - - break; - } - case 'match': { - inputOutputs = value.slice(2); - const fallback = inputOutputs.pop(); - inputOutputs.forEach((val, i) => - i % 2 !== 0 ? outputs.push(val) : inputs.push(val) - ); - outputs.push(fallback); - - const flatCondition = value[1].flat(Infinity); - // TODO this is naive handling to get a property similar to on case expressions - const property = flatCondition.filter(v => !isExpression([v]))[0]; - - // This is a hack for ref length we should handle better - if (flatCondition.includes('length')) { - inputs = inputs.map(num => `${num}`.repeat(num)); - } - - properties = { [property]: inputs.flat(Infinity) }; - break; - } - } - - // If there's a math operation, treat all other values like outputs - // for pulling properties out of recursion - if (mathOperators.includes(expressionType)) { - outputs = value.filter(isHandledConditional); - } - - for (let i = 0; i < outputs.length; i++) { - const output = outputs[i]; - if (isHandledConditional(output)) { - const { - inputs: nestedInputs, - outputs: nestedOutputs, - properties: nestedProperties, - } = parseConditionalExpression(output); - - outputs.splice(i, 1, ...nestedOutputs); - inputs = inputs.concat(nestedInputs); - properties = mergeWith(properties, nestedProperties, mergeWithCustomizer); - } - } - - return { - inputs, - outputs: [...new Set(outputs)], - properties, - }; -}; - -// Gets the referenced data properties and referenced values from expressions -export const getPropertyValues = value => { - let zooms = []; - let properties = {}; - if (isHandledScale(value)) { - ({ zooms, properties } = parseScaleExpression(value)); - } else { - ({ properties } = parseConditionalExpression(value)); - } - - // TODO can we make this more reliable? Sets fallback based on type of value - // If this causes problems, we may be better without fallbacks - // Note: fallbacks defined are _likely_ fallback property values, but not guaranteed based on expression - properties = Object.keys(properties).reduce((acc, prop) => { - let valueType = typeof properties[prop][0]; - if (valueType === 'object' && Array.isArray(valueType)) { - valueType = 'array'; - } - const values = - valueType !== undefined - ? properties[prop].concat([FALLBACKS[valueType]]) - : properties[prop]; - acc[prop] = [...new Set(values)]; - return acc; - }, {}); - - return { - propertyValues: properties, - zooms, - }; -}; - -export const getExpandableProperties = layer => { - return ['paint', 'layout'] - .map(type => { - if (!layer[type]) return []; - return Object.entries(layer[type]) - .map(([key, value]) => { - if (isHandledConditional(value)) return { type, key, value }; - if (isHandledScale(value)) { - return { type, key, value }; - } - }) - .filter(v => v); - }) - .filter(v => v.length) - .reduce((agg, current) => agg.concat(current), []) - .map(v => { - const { propertyValues, zooms } = getPropertyValues(v.value); - return { ...v, properties: propertyValues, zooms }; - }); -}; - -// Returns an expression with non-conditional `get` expressions replaced by the name of the property as a string -// The property list contains all get expressions that are relevant to the conditions -const replaceInternalGets = (value, propertyList) => { - if (!Array.isArray(value)) return value; - if (value[0] === 'get' && !propertyList.includes(value[1])) return value[1]; - return value.map(v => replaceInternalGets(v, propertyList)); -}; - -// Creates new expression for expanded layer based on the properties given -const evaluateExpressionForProperties = ({ - layerType, - paintOrLayout, - propertyId, - value, - properties, - zoom, -}) => { - if (propertyId === 'text-field') { - value = replaceInternalGets(value, Object.keys(properties)); - } - const evaluated = evaluateExpression({ - layerType, - propertyType: paintOrLayout, - propertyId, - properties, - value, - zoom, - }); - return evaluated; -}; - -// Creates new zoom based expression for expanded layer based on the properties given -// Scale expressions are always on the outermost expression -const createEvaluatedZoomExpression = ( - zooms, - { layerType, paintOrLayout, propertyId, value, properties } -) => { - const initialKey = `${paintOrLayout}_${layerType}`; - const propertySpec = latest[initialKey][propertyId]; - const allowsInterpolate = propertySpec?.expression?.interpolated; - - let expression = []; - - if (allowsInterpolate) { - expression.push('interpolate', ['linear'], ['zoom']); - } else { - expression.push('step', ['zoom']); - zooms = [0, ...zooms]; - } - - for (let i = 0; i < zooms.length; i++) { - const zoom = zooms[i]; - let evaluatedExpression = evaluateExpressionForProperties({ - layerType, - paintOrLayout, - propertyId, - value, - properties, - // For evaluation, a zoom of zero returns null - // Unclear why this is - zoom: zoom === 0 ? 0.1 : zoom, - }); - - // The evaluated expression may contain an array, but it's safer to always - // wrap in "literal" since we are building it into a scale expression - if ( - propertyId === 'text-font' && - Array.isArray(evaluatedExpression) && - evaluatedExpression[0] !== 'literal' - ) { - evaluatedExpression = ['literal', evaluatedExpression]; - } - - if (!allowsInterpolate && zoom === 0 && i === 0) { - expression.push(evaluatedExpression); - } - expression.push(zoom, evaluatedExpression); - } - - return expression; -}; - -// Creates a layer for each combination of properties referenced in previous existing layer -export const expandLayer = layer => { - const expandedProperties = getExpandableProperties(layer); - if (!expandedProperties.length) - return { expandedLayers: [layer], comboLimitHit: false }; - let zooms = []; - let propertyPaths = []; - let properties = {}; - - expandedProperties.forEach(property => { - let { - type: propertyType, - key: propertyId, - properties: propertyData, - value: propertyValue, - zooms: propertyZooms, - } = property; - - propertyPaths.push([propertyType, propertyId]); - properties = mergeWith(properties, propertyData, mergeWithCustomizer); - - // Add zooms 0.1 after actual zooms for step functions so that - // we can safely create interpolate functions that look like step functions - // TODO this is naive handling and doesn't account for exponential functions - if (propertyZooms.length) { - const scaleType = propertyValue[0]; - if (scaleType === 'step') { - propertyZooms = propertyZooms.reduce((acc, z) => { - acc.push(z, z + 0.1); - return acc; - }, []); - } - } - - zooms = [...new Set(zooms.concat(propertyZooms))]; - }); - - let propertyCombos = cartesian(properties); - - // Dedupe the combos - propertyCombos = propertyCombos.reduce((acc, combo) => { - const hasCombo = acc.find(item => - Object.keys(item).every(k => combo[k] === item[k]) - ); - if (!hasCombo) { - acc.push(combo); - } - return acc; - }, []); - - const fullComboAmt = propertyCombos.length; - propertyCombos = propertyCombos.slice(0, comboValueLimit); - const slicedComboAmt = propertyCombos.length; - - let nextLayers = []; - - for (const combo of propertyCombos) { - const nextLayer = JSON.parse(JSON.stringify(layer)); - - let nextId = Object.keys(combo).reduce((acc, prop) => { - const propValue = - FALLBACKS[combo[prop]] !== undefined ? 'FALLBACK' : combo[prop]; - acc.push([prop, ': ', JSON.stringify(propValue)].join('')); - return acc; - }, []); - nextId = `${nextLayer.id}/${nextId.join('/')}`; - nextLayer.id = nextId; - for (const path of propertyPaths) { - const [paintOrLayout, propertyId] = path; - let nextValue = nextLayer[paintOrLayout][propertyId]; - const args = { - layerType: layer.type, - paintOrLayout, - propertyId, - value: nextValue, - properties: combo, - }; - - if (zooms.length) { - nextValue = createEvaluatedZoomExpression(zooms, args); - } else { - nextValue = evaluateExpressionForProperties(args); - } - // If next value is invalid, then remove the property - if (nextValue === null) { - delete nextLayer[paintOrLayout][propertyId]; - } else { - nextLayer[paintOrLayout][propertyId] = nextValue; - } - } - - nextLayers.push(nextLayer); - } - - return { - expandedLayers: nextLayers, - comboLimitHit: fullComboAmt > slicedComboAmt, - }; -}; diff --git a/src/styles/styles-equal.js b/src/styles/styles-equal.js deleted file mode 100644 index 642b46b..0000000 --- a/src/styles/styles-equal.js +++ /dev/null @@ -1,15 +0,0 @@ -import isEqual from 'lodash.isequal'; -import { migrate } from '@mapbox/mapbox-gl-style-spec'; -import { convertStylesheetToRgb } from '../convert-colors'; - -const stylesEqual = (styleA, styleB) => { - if ((styleA && !styleB) || (styleB && !styleA)) return false; - if (!styleA && !styleB) return true; - - styleA = convertStylesheetToRgb(migrate(styleA)); - styleB = convertStylesheetToRgb(migrate(styleB)); - - return isEqual(styleA, styleB); -}; - -export { stylesEqual }; diff --git a/static/.nojekyll b/static/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/public/CNAME b/static/CNAME similarity index 100% rename from public/CNAME rename to static/CNAME diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..781d740 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,29 @@ +import adapter from '@sveltejs/adapter-static'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + // Enable SCSS preprocessing so