diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 70f9eaeb2..000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[registries.crates-io] -protocol = "sparse" diff --git a/.github/workflows/build-preview.yml b/.github/workflows/build-preview.yml new file mode 100644 index 000000000..84d2ac002 --- /dev/null +++ b/.github/workflows/build-preview.yml @@ -0,0 +1,129 @@ +name: Build PR Preview +on: + pull_request: + branches: [main] + types: + - opened + - reopened + - synchronize + workflow_dispatch: +permissions: + contents: read + pages: write + pull-requests: write + id-token: write + checks: write +concurrency: preview-${{ github.ref }} +jobs: + build-preview: + runs-on: ubuntu-latest + steps: + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + - name: Checkout Main Branch 🛎️ + uses: actions/checkout@v4 + with: + ref: main + path: main + - name: Checkout PR Branch 🛎️ + uses: actions/checkout@v4 + with: + path: pr-branch + - name: Install and Build Main Branch 🔧 + run: | + npm ci --include=dev + npm run build + npm run style + npm run shields + cp src/configs/config.aws.js src/config.js + working-directory: main + - name: Install and Build PR Branch 🔧 + # TODO: when we move shieldlib to its own repo, move shieldlib docs CI also + run: | + npm ci --include=dev + npm run build + npm run style + npm run shields + cp src/configs/config.aws.js src/config.js + mkdir -p dist/shield-docs + cp -r shieldlib/docs/* dist/shield-docs + mv dist .. + working-directory: pr-branch + - name: Capture main branch usage statistics + id: main-stats + run: | + MAIN_STATS=$(node ../pr-branch/scripts/stats.js -j) + echo "MAIN_STATS<> $GITHUB_ENV + echo -e "$MAIN_STATS" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + working-directory: main + - name: Start main branch map server + id: main-start-server + run: | + npm start & + until nc -z localhost 1776; do sleep 1; done + working-directory: main + - name: Capture main branch sample clips + # Run PR branch scripts against server running from main branch + id: main-samples + run: | + npm run generate_samples + mv samples ../samples-main + working-directory: pr-branch + - name: Stop main branch map server + id: main-stop-server + run: kill $(lsof -t -i:1776) 2>/dev/null || echo "No process found listening on port 1776." + working-directory: main + - name: Capture PR branch sample clips + id: pr-samples + run: | + npm start & + until nc -z localhost 1776; do sleep 1; done + npm run generate_samples + kill $(lsof -t -i:1776) 2>/dev/null || echo "No process found listening on port 1776." + mv samples ../samples-pr + working-directory: pr-branch + - name: Capture PR branch usage statistics + id: pr-stats + run: | + PR_STATS=$(node scripts/stats.js -j) + echo "PR_STATS<> $GITHUB_ENV + echo -e "$PR_STATS" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + working-directory: pr-branch + - name: Compare Stats + id: compare-stats + run: | + mkdir -p ../pr + echo '${{ env.MAIN_STATS }}' + echo '${{ env.PR_STATS }}' + npm exec tsx scripts/stats_compare '${{ env.MAIN_STATS }}' '${{ env.PR_STATS }}' > ../pr/stats-difference.md + working-directory: pr-branch + - name: Generate map diff sample clips + id: map-diff-samples + run: | + npm exec tsx scripts/folder_diff ../samples-main ../samples-pr https://preview.ourmap.us/pr/${{ github.event.pull_request.number }}/ ${{ github.event.pull_request.head.sha }} + mv pr_preview-extra.md ../pr/ + cat ../pr/pr_preview-extra.md + mv samples-diff ../dist/ + working-directory: pr-branch + - name: Upload Build artifacts + uses: actions/upload-artifact@v4 + with: + name: americana + path: dist/ + - name: Save PR attributes + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_SHA: ${{ github.event.pull_request.head.sha }} + run: | + echo $PR_NUMBER > pr/pr_number + echo $PR_SHA > pr/pr_sha + echo "Saved PR# ${{ github.event.pull_request.number }}, SHA# ${{ github.event.pull_request.head.sha }} for upload" + - name: Upload PR artifacts + uses: actions/upload-artifact@v4 + with: + name: pr_ci_artifacts + path: pr/ diff --git a/.github/workflows/deploy-pr-checks.yml b/.github/workflows/deploy-pr-checks.yml new file mode 100644 index 000000000..447659185 --- /dev/null +++ b/.github/workflows/deploy-pr-checks.yml @@ -0,0 +1,143 @@ +name: Deploy PR Preview +on: + workflow_run: + workflows: [Build PR Preview] + types: + - completed + workflow_dispatch: +jobs: + deploy-preview: + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: "Download PR Number" + uses: actions/github-script@v6 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr_ci_artifacts" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr_ci_artifacts.zip`, Buffer.from(download.data)); + - name: "Download Build" + uses: actions/github-script@v6 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "americana" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/americana.zip`, Buffer.from(download.data)); + - name: "Unzip artifacts" + run: | + unzip pr_ci_artifacts.zip + unzip -d dist americana.zip + - name: Set PR variable + run: | + if [ ! -f pr_number ] || [ ! -f pr_sha ]; then + echo "Required files pr_number or pr_sha do not exist. Exiting." + exit 1 + fi + PR_NUM=$(cat pr_number) + PR_SHA=$(cat pr_sha) + if [ -z "$PR_NUM" ] || [ -z "$PR_SHA" ]; then + echo "PR_NUM or PR_SHA is empty. Exiting." + exit 1 + fi + echo "Configuring deployment for PR# $PR_NUM commit $PR_SHA" + echo "PR_NUM=$PR_NUM" >> $GITHUB_ENV + echo "PR_SHA=$PR_SHA" >> $GITHUB_ENV + - name: Deploy via S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --follow-symlinks --delete --exclude '.git/*' --exclude '.github/*' + env: + AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + SOURCE_DIR: "./dist" + DEST_DIR: pr/${{ env.PR_NUM }} + - name: Wait for PR Preview Upload (1x Sprite) + uses: cygnetdigital/wait_for_response@v2.0.0 + with: + url: "https://preview.ourmap.us/pr/${{ env.PR_NUM }}/sprites/sprite.png" + responseCode: "200" + timeout: 120000 + interval: 500 + - name: Wait for PR Preview Upload (2x Sprite) + uses: cygnetdigital/wait_for_response@v2.0.0 + with: + url: "https://preview.ourmap.us/pr/${{ env.PR_NUM }}/sprites/sprite@2x.png" + responseCode: "200" + timeout: 120000 + interval: 500 + - name: Generate Preview text + run: | + echo "## Live PR Preview: + * [Map](https://preview.ourmap.us/pr/${{ env.PR_NUM }}/) + * [Shield Test](https://preview.ourmap.us/pr/${{ env.PR_NUM }}/shieldtest.html) + * [style.json](https://preview.ourmap.us/pr/${{ env.PR_NUM }}/style.json) + * [shields.json](https://preview.ourmap.us/pr/${{ env.PR_NUM }}/shields.json) + * [taginfo.json](https://preview.ourmap.us/pr/${{ env.PR_NUM }}/taginfo.json) + + Live previews are automatically removed once the PR is merged. + + ## Sprite Sheets: + + + + " > pr_preview.md + [ -f pr_preview-extra.md ] && cat pr_preview-extra.md >> pr_preview.md || echo "No extra PR preview found." + - uses: tibdex/github-app-token@v1 + id: checks_token + with: + app_id: 396440 #osm-americana checks app + private_key: ${{ secrets.CHECKS_WRITER_SECRET }} + - name: Print Preview Links to GitHub Checks + uses: LouisBrunner/checks-action@v1.6.1 + if: always() + with: + sha: ${{ env.PR_SHA }} + token: ${{ steps.checks_token.outputs.token }} + name: PR Preview + details_url: https://preview.ourmap.us/pr/${{ env.PR_NUM }}/ + conclusion: neutral + output: | + {"summary":"Preview map changes introduced by this PR", "title":"View live map and artifacts"} + output_text_description_file: pr_preview.md + - name: Print Stats to GitHub Checks + uses: LouisBrunner/checks-action@v1.6.1 + if: always() + with: + sha: ${{ env.PR_SHA }} + token: ${{ steps.checks_token.outputs.token }} + name: Style Performance + conclusion: neutral + output: | + {"summary":"Style size changes introduced by this PR", "title":"View metrics on style size changes"} + output_text_description_file: stats-difference.md diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9bd325724..fc8537a81 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,9 @@ permissions: contents: read pages: write id-token: write +concurrency: preview-${{ github.ref }} jobs: + # TODO remove old PRs from s3 bucket automatically build-and-deploy: runs-on: ubuntu-latest environment: @@ -17,21 +19,25 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x - name: Install and Build 🔧 + # TODO: when we move shieldlib to its own repo, move shieldlib docs CI also run: | npm ci --include=dev cp src/configs/config.aws.js src/config.js npm run build npm run style + npm run shields + mkdir -p dist/shield-docs + cp -r shieldlib/docs/* dist/shield-docs - name: Upload 🏗 - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: ./dist - name: Deploy 🚀 id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 04f98f9bb..5d1c098d2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Make sure the actual branch is checked out when running on pull requests repository: ${{ github.event.pull_request.head.repo.full_name }} @@ -18,12 +18,19 @@ jobs: # This is important to fetch the changes to the previous commit fetch-depth: 0 - - name: Prettify code - uses: creyD/prettier_action@v3.3 # https://github.com/creyD/prettier_action + - name: Set up Node.js + uses: actions/setup-node@v4 with: - dry: true - prettier_options: --write . - # Setting only_changed to false picks up new files - only_changed: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + node-version: "18" # Specify the Node.js version + + - name: Install dependencies + run: npm install + + - name: Run Prettier + id: prettier + run: npx prettier --check . + continue-on-error: false + + - name: Check if Prettier failed + if: steps.prettier.outcome == 'failure' + run: exit 1 diff --git a/.github/workflows/s3-cleanup.yml b/.github/workflows/s3-cleanup.yml new file mode 100644 index 000000000..5d5b45acc --- /dev/null +++ b/.github/workflows/s3-cleanup.yml @@ -0,0 +1,24 @@ +name: Delete S3 Files on PR Merged + +on: + pull_request: + types: + - closed + +jobs: + delete-s3-files: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + steps: + - name: Set up AWS CLI + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Remove files from S3 + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + aws s3 rm s3://${{ secrets.AWS_S3_BUCKET }}/pr/${PR_NUMBER}/ --recursive diff --git a/.github/workflows/test-build-mac.yml b/.github/workflows/test-build-macos-13.yml similarity index 54% rename from .github/workflows/test-build-mac.yml rename to .github/workflows/test-build-macos-13.yml index ff9d7e0ed..3d893b395 100644 --- a/.github/workflows/test-build-mac.yml +++ b/.github/workflows/test-build-macos-13.yml @@ -1,4 +1,4 @@ -name: Test Build (MacOS) +name: Test Build (MacOS 13) on: pull_request: @@ -6,14 +6,19 @@ on: jobs: build: - runs-on: macos-latest + runs-on: macos-13 steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x + - name: Build Shield Library 🛡️ + run: | + cd shieldlib + npm ci --include=dev + npm run build - name: Install and Build 🔧 run: | npm ci --include=dev @@ -22,3 +27,5 @@ jobs: - name: Test 🧪 run: | npm test + cd shieldlib + npm test diff --git a/.github/workflows/test-build-ubuntu.yml b/.github/workflows/test-build-ubuntu.yml index ab52c7603..4440ba9c6 100644 --- a/.github/workflows/test-build-ubuntu.yml +++ b/.github/workflows/test-build-ubuntu.yml @@ -9,11 +9,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x + - name: Build Shield Library 🛡️ + run: | + cd shieldlib + npm ci --include=dev + npm run build - name: Install and Build 🔧 run: | npm ci --include=dev @@ -22,3 +27,5 @@ jobs: - name: Test 🧪 run: | npm test + cd shieldlib + npm test diff --git a/.gitignore b/.gitignore index 044358e38..7fc8e9cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ build .DS_Store config.js .parcel-cache -dist/ -download/ +dist +download local.config.js +shieldlib/docs +samples/ diff --git a/.mocharc.yml b/.mocharc.yml index 7fd0cbb99..2a41056a3 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -1 +1,7 @@ recursive: true +extension: + - ts +spec: + - test/**/*.js + - test/**/*.spec.ts +import: tsx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 982678e60..6c4e9a7e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,6 +127,10 @@ server which is beyond the scope of this guide. [20]: https://cloud.maptiler.com/maps/ [21]: https://openmaptiles.org/schema/ +### Running your own tile server + +For production usages, consider running your own tile server. Thanks to advances in technology, it is possible to run an OpenMapTiles tile server for a modest cost. See the diary entry [Host an OpenMapTiles Vector Tile Server on AWS for $19.75/month](https://www.openstreetmap.org/user/ZeLonewolf/diary/401697) for more details. + ## Running the Americana style ### In development... @@ -186,13 +190,19 @@ When adding or changing style layer code, it can be helpful to assess the change There is a "stats" script that will generate various statistics about layer composition and complexity: -- `npm run stats -- -a -s` - overall size and breakdown of layers -- `npm run stats -- -c` - total layer count -- `npm run stats -- -h` - list all options +- `npm run -s stats -- -a -s` - overall size and breakdown of layers +- `npm run -s stats -- -c` - total layer count +- `npm run -s stats -- -h` - list all options + +There is an "extract_layers" script that will extract layer style data: + +- `npm run -s extract_layer -pl ` - JSON contents of a specified layer +- `npm run -s extract_layer -pg ` - list of layers from a specified source +- `npm run -s extract_layer -h` - list all options ## Layers -1. Layers should be named as followed: `_`, wher the "group" should match the file name that the layer is contained in. This naming convention is needed by the layer statistic script. +1. Layers must be uniquely named. 2. For performance reasons, it is better to have fewer layers with filters than multiple, simpler layers. 3. Layers are drawn in the order specified in `layer/index.js` using the [Painter's Algorithm](https://en.wikipedia.org/wiki/Painter%27s_algorithm). @@ -220,35 +230,51 @@ In addition, the following style guidelines are recommended: In general, this style is not trying to exactly replicate highway shields as seen on signage. Instead, we are trying to extract the key stylistic elements so that the graphics are recognizable as simplifications of their real-world counterparts. Here are some examples of Americana's simplified shields for small-size readability: -| Network | Real-world Shield | Americana Representation | -| ---------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| NJ Turnpike | | | -| Washington State Route | | | +| Network | Real-world Shield | Americana Representation | +| ---------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | +| NJ Turnpike | | | +| Washington State Route | | | More complicated shields may be more challenging to simplify. Consider taking some inspiration from the [rebusurance](https://github.com/1ec5/rebusurance) project, which effectivly simplifies a number of complex state shield designs by stretching, compressing, omitting, or simplifying graphic elements. ### Color Scheme -For consistency, shields should use the following color palette: +For consistency, most shields should use the following color palette, which is based on the national _Manual of Uniform Traffic Control Devices_:[^mutcd-color-specs] -| color | Pantone | RGB | Hex triplet | -| --------------------------------------------------------------------- | ------- | ----------- | ----------- | -| Blue | 294 | 0 63 135 | #003f87 | -| Brown | 469 | 105 63 35 | #693f23 | -| Green | 342 | 0 103 71 | #006747 | -| Orange | 152 | 243 143 0 | #f38f00 | -| Pink | 198 | 223 70 97 | #df4661 | -| Purple | 259 | 109 32 119 | #6d2077 | -| Red | 187 | 191 32 51 | #bf2033 | -| Yellow | 116 | 255 205 0 | #ffcd00 | -| Yellow-Green | 382 | 196 214 0 | #c4d600 | -| White | N/A | 255 255 255 | #ffffff | -| Black | N/A | 0 0 0 | #000000 | +| Color | Pantone | RGB | Hex triplet | +| ----------------------------------------------- | ------- | ----------- | ----------- | +| $`{\color{#003f87} \blacksquare}`$ Blue | 294 | 0 63 135 | #003f87 | +| $`{\color{#693f23} \blacksquare}`$ Brown | 469 | 105 63 35 | #693f23 | +| $`{\color{#006747} \blacksquare}`$ Green | 342 | 0 103 71 | #006747 | +| $`{\color{#f38f00} \blacksquare}`$ Orange | 152 | 243 143 0 | #f38f00 | +| $`{\color{#df4661} \blacksquare}`$ Pink | 198 | 223 70 97 | #df4661 | +| $`{\color{#6d2077} \blacksquare}`$ Purple | 259 | 109 32 119 | #6d2077 | +| $`{\color{#bf2033} \blacksquare}`$ Red | 187 | 191 32 51 | #bf2033 | +| $`{\color{#ffcd00} \blacksquare}`$ Yellow | 116 | 255 205 0 | #ffcd00 | +| $`{\color{#c4d600} \blacksquare}`$ Yellow-green | 382 | 196 214 0 | #c4d600 | +| $`{\color{#ffffff} \blacksquare}`$ White | N/A | 255 255 255 | #ffffff | +| $`{\color{#000000} \blacksquare}`$ Black | N/A | 0 0 0 | #000000 | -Base data from MUTCD Color Specifications, Federal Highway Administration +[^mutcd-color-specs]: [MUTCD Color Specifications](https://mutcd.fhwa.dot.gov/kno-colorspec.htm), Federal Highway Administration + +The MUTCD’s standard colors are designed for high-contrast backgrounds and legends. This is also advantageous on a map where shields need to stand out a variety of lines and fills. However, tourist and scenic route shield often depict natural scenes in a photorealistic style, requiring some tints that stand out against the usual background shades. These shields can take advantage of additional colors for contrast and recognizability, including:[^caltrans-sign-specs] + +| Color and example usage | Pantone | RGB | Hex triplet | +| ---------------------------------------------------------- | ------- | ----------- | ----------- | +| $`{\color{#ddcba4} \blacksquare}`$ Cream/tan (wood) | 468 | 221 203 164 | #ddcba4 | +| $`{\color{#9bcbeb} \blacksquare}`$ Light blue (sky, water) | 291 | 155 203 235 | #9bcbeb | +| $`{\color{#6cc24a} \blacksquare}`$ Light green (foliage) | 360 | 108 194 74 | #6cc24a | + +[^caltrans-sign-specs]: [California Sign Specification Drawings](https://dot.ca.gov/programs/safety-programs/sign-specs), California Department of Transportation See the [developer tools](dev/README.md) for an importable, Inkscape-compatible palette file. +### Icon Grid Alignment + +There is a utility script called icon_grid that will generate a pixel grid on an SVG. This can be used to check how well the icon will align to the pixel grid. Run this utility as follows: + +`npm run icon_grid -- icons/poi_fuel.svg` + ### Font Sizes Shields should target 8-14px text actual-size character heights for readability: @@ -282,17 +308,19 @@ The `loadShields` function in js/shield_defs.js contains a definition object for In addition to `textHaloColor`, the config variable **`SHIELD_TEXT_HALO_COLOR_OVERRIDE`** can be used to override the text halo color on all shields. This can be helpful to avoid collisions with other design features when determining padding values. For example, set `SHIELD_TEXT_HALO_COLOR_OVERRIDE` in src/config.js to `"magenta"` to display a magenta halo around all shield text. -If special code is necessary to style a route with a specific `ref` or `name` in a particular network, **`overrideByRef`** or **`overrideByWayName`** can be used to define and override any of the above properties. `overrideByRef` is an object mapping `ref` values to partial shield definition objects, containing whichever properties are to be overridden for that particular `ref` value. `overrideByWayName` does the same for `name` values. If necessary, these parameters can be used to override the entire shield definition. +If special code is necessary to style a route with a specific `ref` or `name` in a particular network, **`overrideByRef`** or **`overrideByName`** can be used to define and override any of the above properties. `overrideByRef` is an object mapping `ref` values to partial shield definition objects, containing whichever properties are to be overridden for that particular `ref` value. `overrideByName` does the same for `name` values. If necessary, these parameters can be used to override the entire shield definition. -Additionally, **`refsByWayName`** is an object mapping way names to text that can be superimposed on the background as a fallback for a missing `ref` value. (`refsByWayName` implies `notext`.) This temporary fallback is designed for use in [limited situations](https://wiki.openstreetmap.org/wiki/United_States/Unusual_highway_networks). In the future, it is expected that these initialisms will be encoded on the server side by processing appropriate tagging which holds the initialism in the database. +Additionally, **`refsByName`** is an object mapping way names to text that can be superimposed on the background as a fallback for a missing `ref` value. (`refsByName` implies `notext`.) This temporary fallback is designed for use in [limited situations](https://wiki.openstreetmap.org/wiki/United_States/Unusual_highway_networks). In the future, it is expected that these initialisms will be encoded on the server side by processing appropriate tagging which holds the initialism in the database. -`refsByWayName` only works if there is no `ref` tag and the expression in the `routeConcurrency` function in layer/highway_shield.js includes the `name` property in the image name. The network needs to be listed as an input value that causes the `match` expression to append `name` to the image name. +`refsByName` only works if there is no `ref` tag and the expression in the `routeConcurrency` function in layer/highway_shield.js includes the `name` property in the image name. The network needs to be listed as an input value that causes the `match` expression to append `name` to the image name. -When using `overrideByRef` or `refsByWayName`, make sure to add a line to the Special Cases section of this page explaining why it is necessary, as they are only intended for use in special cases. +When using `overrideByRef` or `refsByName`, make sure to add a line to the Special Cases section of this page explaining why it is necessary, as they are only intended for use in special cases. + +In the case where all routes in a network should be drawn with the same shield text, set the text value in `ref`. ### Banners -The shield definition supports a property **`modifiers`** which accepts an array of text strings which will be drawn atop each shield, in 10px height increments. This is used in cases where additional text is needed to differentiate shields with a common symbology, for example for [special routes of the US Numbered Highway System](https://en.wikipedia.org/wiki/List_of_special_routes_of_the_United_States_Numbered_Highway_System): +The shield definition supports a property **`banners`** which accepts an array of text strings which will be drawn atop each shield, in 10px height increments. This is used in cases where additional text is needed to differentiate shields with a common symbology, for example for [special routes of the US Numbered Highway System](https://en.wikipedia.org/wiki/List_of_special_routes_of_the_United_States_Numbered_Highway_System):
@@ -321,6 +349,7 @@ This style strives to draw representative highway shields wherever they are tagg - **Kentucky Parkways**. Kentucky signs a network of state highways which use a common shield styling, but with full-text names of the parkways on the shields. In addition, these routes are locally known by initialisms. Because these parkways are clearly a common network due to their common shield symbology, special code is needed to convert parkway names to their locally-expected initialisms. Because the initialisms are not present on shields, it would not be appropriate to encode this data in the `ref` tag. - **New York Parkways**. The State of New York signs a network of highways which use a common shield styling, but with full-text names of the parkways on the shields. The first letter of each word in a parkway's name is capitalized and in a larger font, making initialisms easily recognizable. Because these parkways are clearly a common network due to their common shield symbology, special code is needed to convert parkway names to their initialisms. Because the initialisms are present on shields, but only as part of the full name, it would not be appropriate to encode this data in the `ref` tag. - **Connecticut Parkways**. Connecticut has several state-designated parkways that share the `network=US:CT:Parkway` tag but have no parkway-specific `ref` tags. The Merritt Parkway is the only of these to be signed with a route shield. Special code is needed to differentiate the Merritt from the state's other parkways. + - **New Hampshire Turnpikes**. New Hampshire has three named turnpikes without unique `ref=` values. One turnpike is unsigned, while the other two use a shield with the full name of the turnpike and a color for each turnpike. - Shields for route networks where each individual route is identified by a color, rather than a number or letter. Such cases include: - **Allegheny County, PA Belt Routes**. Shields for this system use colors, with a colored circle and the words " BELT". These shields are drawn as squares with colored circles, with the `ref` values correctly corresponding to the text on the shield. Because of the common design (white shield with colored circle), these shields are properly part of a common route network. Special code is needed to convert the textual ref values to the colors displayed in the shield. - **Branson, MO color-coded routes**. Shields for this system use colors, with a colored rectangle and the words " ROUTE". These shields are drawn as squares with colored rectangles, with the `ref` values correctly corresponding to the text on the shield. Because of the common design (green shield with colored rectangle), these shields are properly part of a common route network. Special code is needed to convert the textual ref values to the colors displayed in the shield. @@ -370,13 +399,21 @@ POIs are broken down into the following broad categories, in order to constrain For consistency, POI icons use the following color palette: -| Category | Scheme | Color | RGB | Hex triplet | -| ---------------------- | --------------- | --------------------------------------------------------------------------- | ----------- | ----------- | -| Geographic Place Names | N/A | Black | 0 0 0 | #000000 | -| Infrastructure | Pantone 294 | Blue | 0 63 135 | #003f87 | -| Consumer | UTexas Orange | Orange | 191 87 0 | #bf5700 | -| Outdoor | | TBD (green?) | | | -| Attraction | Pantone 469 | Brown | 105 63 35 | #693f23 | -| Airport | Medium Purple C | Purple | 78 0 142 | #4e008e | -| Transport | Pantone 234 C | Mauve | 162 0 103 | #a20067 | -| Knockout | | Lt Grayish Orange | 249 245 240 | #f9f5f0 | +| Category | Scheme | Color | RGB | Hex triplet | +| ---------------------- | --------------- | ---------------------------------------------------- | ----------- | ----------- | +| Geographic Place Names | N/A | $`{\color{#000000} \blacksquare}`$ Black | 0 0 0 | #000000 | +| Infrastructure | Pantone 294 | $`{\color{#003f87} \blacksquare}`$ Blue | 0 63 135 | #003f87 | +| Consumer | UTexas Orange | $`{\color{#bf5700} \blacksquare}`$ Orange | 191 87 0 | #bf5700 | +| Outdoor | | TBD (green?) | | | +| Attraction | Pantone 469 | $`{\color{#693f23} \blacksquare}`$ Brown | 105 63 35 | #693f23 | +| Airport | Medium Purple C | $`{\color{#4e008e} \blacksquare}`$ Purple | 78 0 142 | #4e008e | +| Transport | Pantone 234 C | $`{\color{#a20067} \blacksquare}`$ Mauve | 162 0 103 | #a20067 | +| Knockout | | $`{\color{#f9f5f0} \blacksquare}`$ Lt Grayish Orange | 249 245 240 | #f9f5f0 | + +## Fonts + +Fonts for style labels are packaged and defined in [fontstack66](https://github.com/osm-americana/fontstack66), Americana's font package. + +## Render Samples + +A GitHub action will check a list of regression test locations to see if the map has changed. If any of those locations have changed visually, the "Map Preview" check will generate before and after images. If your PR changes the visual appearance of the map, add an entry to `test/sample_locations.json` with a location that best illustrates the change. This will help show your change to PR reviewers as well as act as a regression test for future PRs. diff --git a/README.md b/README.md index 75addefd4..2c029dfad 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,18 @@ _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.g The purpose of the Americana style is to: -- Promote collaboration and common purpose in the American mapping community +- Promote collaboration and common purpose in OpenStreetMap’s American mapping community - Express the American experience through cartography, taking inspiration from the familiar features of North American paper maps - Challenge the status quo by showcasing innovation and invention -The Americana style is the first digital map to achieve concurrent, state-specific highway shields arranged along the path of road. Representative highway shield rendering is of considerable cartographic importance to the American community. We do this proudly in an open source project using vector tile technology. +Americana has demonstrated or pioneered several cartographic techniques of importance to Americans that we hope will someday become commonplace among OpenStreetMap-based map styles: + +- Nuanced line styles help you distinguish roads, raiload tracks, and waterways based on a variety of intuitive characteristics. +- Highway routes are identified by shields that resemble the signs on the road, with special support for roads that carry multiple routes concurrently. +- Place labels throughout the world appear in both your preferred language and the local language, reflecting linguistic diversity both in the U.S. and abroad. +- A dynamic legend communicates these design choices intuitively, regardless of the visual language you’re accustomed to. + +We do this proudly in an open source project using vector tile technology. ## How to use @@ -31,7 +38,7 @@ The repository is organized as follows: - **src/** - The map style. See [CONTRIBUTING.md](CONTRIBUTING.md). - **dev/** - Development tools used for style development. See [Style Developer Tools](dev/README.md) - **test/** - Automated unit tests. -- _Coming soon! Other customized parts of the tech stack._ +- **shieldlib/** - Maplibre [shield rendering library](shieldlib/README.md) ([npm entry](https://www.npmjs.com/package/@americana/maplibre-shield-generator?activeTab=readme)). Some general guidelines: @@ -47,6 +54,14 @@ The technology stack for this style can be summarized below: Americana technology stack +The dynamic shield generator is included as a [module](shieldlib/README.md) in this repository and also [published to npm](https://www.npmjs.com/package/@americana/maplibre-shield-generator). + +## Artifacts + +- Style users can use the maplibre [StyleJSON](https://zelonewolf.github.io/openstreetmap-americana/style.json), and sprite sheets ([1x](https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite.png), [2x](https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite@2x.png)). +- For highway shield library users, a [ShieldJSON](https://zelonewolf.github.io/openstreetmap-americana/shields.json) must be supplied to associate route networks with sprite images and drawn shield shapes. +- The project [taginfo.json](https://zelonewolf.github.io/openstreetmap-americana/taginfo.json) lists which tags are used by the style. + ## Data sources The OpenStreetMap Americana style is built upon the [OpenMapTiles schema](https://openmaptiles.org/schema/), which includes: @@ -66,3 +81,7 @@ Americana displays custom route shields for routes in all U.S. states and territ Countries We are hoping that it will support more countries; you can [help us](https://github.com/ZeLonewolf/openstreetmap-americana/projects/1)! + +## Tile Server + +Although the source code in this repository is dedicated to the public domain under a CC0 waiver, it is configured by default to load map tiles from a privately-donated community [tile server](https://tile.ourmap.us). This server is available for use by other hobbyist and community projects with constraints. See the [tile server usage policy](TILE_USAGE.md) for more details. This usage policy describes the allowable usage of the tiles for other projects, separate from this style or the application being developed in this repository. diff --git a/TILE_USAGE.md b/TILE_USAGE.md new file mode 100644 index 000000000..6b818ca57 --- /dev/null +++ b/TILE_USAGE.md @@ -0,0 +1,41 @@ +# Tile Usage Policy + +Tile Usage Policy for OpenStreetMap Americana Community Vector Tile Server + +**Effective Date: June 12, 2023** + +Thank you for using the OpenStreetMap Americana [Community Vector Tile Server](https://tile.ourmap.us) (referred to as "the Server"). By accessing and using the tiles provided by the Server, you agree to comply with the following Tile Usage Policy: + +1. Purpose: + The Server aims to support hobbyists, small community groups, and individuals who require vector tiles for their projects or development of custom map styles. The Server is a community resource provided at no cost, but limitations may apply based on server capacity and financial constraints. + +2. Attribution: + When using tiles from the Server, you must provide proper attribution to [OpenStreetMap](https://wiki.osmfoundation.org/wiki/Licence/Attribution_Guidelines#OpenStreetMap_Attribution_Guideline) and [OpenMapTiles](https://github.com/openmaptiles/openmaptiles/blob/master/LICENSE.md) as required by their respective attribution policies. + +3. Personal and Non-Commercial Use: + The Server is primarily intended for personal, non-commercial use. You are welcome to use the tiles for personal or community projects, educational purposes, or non-profit initiatives that do not generate revenue. There are several [guides](https://www.openstreetmap.org/user/ZeLonewolf/diary/401697) available for commercial users that desire to run a vector tile server at low cost. Note that this policy only applies to the tile server usage. Anyone is free to use the OSM Americana on their own tile server, even for a commercial endeavor. + +4. Domain Whitelisting: + Tile usage from localhost and \*.github.io is permitted, which are common ways that most developers will access the tile server when starting a new project. If you wish to use the tiles under your own domain name, please contact the Americana team on [Slack](https://osmus.slack.com/archives/C01V02K52UX) (preferred) or [GitHub](https://github.com/ZeLonewolf/openstreetmap-americana) and provide your domain name(s) in order to add your domain to the CORS (Cross-Origin Resource Sharing) white list. This will allow you to fetch tiles directly from your domain. This whitelist is subject to the discretion and may be modified or removed at any time, however, permission is routinely given for community and hobby projects. + +5. Usage Limits: + To ensure fair usage and prevent abuse, the Server may enforce limits on tile requests for individual users or domains. If your usage exceeds reasonable levels or begins to strain the server's resources, the administrator reserves the right to restrict or terminate access to the Server. Users whose usage becomes excessive may be requested to set up their own tile server. + +6. Financial Considerations: + The Server is operated on a limited budget and funded out of pocket by the administrator. While it strives to be freely accessible, financial constraints may necessitate restrictions on usage. If the costs associated with providing tiles become prohibitive, the administrator may need to limit or suspend service temporarily or permanently. Users who can afford to host their own tile server are encouraged to do so. + +7. No Guarantees: + The Server is provided on an "as-is" basis, and no guarantees or warranties are offered regarding its availability, performance, or accuracy of the tiles. The administrator will make reasonable efforts to maintain the service, but interruptions or downtime may occur due to various factors beyond their control. + +8. Modifications and Termination: + The administrator reserves the right to modify or terminate the Server or this Tile Usage Policy at any time without prior notice. The administrator may also modify or update the CORS white list as needed. + +9. Compliance with OpenStreetMap Guidelines: + All users of the Server are expected to comply with the OpenStreetMap licensing terms, guidelines, and community norms. Additionally, the OpenStreetMap Americana project has adopted the OpenStreetMap US Code of Conduct, which applies where applicable. Any violation of these terms may result in the immediate termination of access to the Server. + +10. Feedback and Support: + The administrator appreciates feedback, bug reports, and suggestions to improve the Server via [Slack](https://osmus.slack.com/archives/C01V02K52UX) or [GitHub]. However, support resources may be limited. Users are encouraged to seek assistance from the OpenStreetMap community or other relevant forums for additional support. + +By using the tiles provided by the OpenStreetMap Americana Community Vector Tile Server, you acknowledge and agree to the terms outlined in this Tile Usage Policy. Failure to comply with these terms may result in the revocation of access to the Server. + +Please note that this Tile Usage Policy is subject to change at any time and for any reason. diff --git a/dev/README.md b/dev/README.md index 2ec39e467..5c81836a3 100644 --- a/dev/README.md +++ b/dev/README.md @@ -11,3 +11,12 @@ The highway shield [color palette](americana.gpl) can be imported into Inkscape 3. Import [americana.gpl](americana.gpl). 4. Restart Inkscape. 5. Click the ◀ button to the right of the color palette strip at the bottom of the window (or in the top-right corner of the Color Palette panel), then choose americana.gpl from the menu. + +## Map sample images + +Map sample images can be generated with a script. See [sample_locations.json](test/sample_locations.json) for the format. + +1. Create a JSON file with the map location and clipping rectangles +2. Start the server in the background with `npm start &` +3. Configure playwright: either `npx playwright install chromium` or `export CHROME_BIN=/usr/bin/chromium` +4. Run the generate_samples script and pass the JSON file location: `npm run generate_samples -- chrome test/sample_locations.json``. You may also use "firefox" or "safari". diff --git a/doc-img/background.svg b/doc-img/background.svg deleted file mode 100644 index d67350c0c..000000000 --- a/doc-img/background.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/black.svg b/doc-img/black.svg deleted file mode 100644 index e83333867..000000000 --- a/doc-img/black.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_116.svg b/doc-img/pantone_116.svg deleted file mode 100644 index f6c602ce3..000000000 --- a/doc-img/pantone_116.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_152.svg b/doc-img/pantone_152.svg deleted file mode 100644 index 3010d151f..000000000 --- a/doc-img/pantone_152.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_187.svg b/doc-img/pantone_187.svg deleted file mode 100644 index c0f76bd99..000000000 --- a/doc-img/pantone_187.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_198.svg b/doc-img/pantone_198.svg deleted file mode 100644 index c7d353832..000000000 --- a/doc-img/pantone_198.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_234_c.svg b/doc-img/pantone_234_c.svg deleted file mode 100644 index e9882380d..000000000 --- a/doc-img/pantone_234_c.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - diff --git a/doc-img/pantone_259.svg b/doc-img/pantone_259.svg deleted file mode 100644 index 7e9bcf563..000000000 --- a/doc-img/pantone_259.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_294.svg b/doc-img/pantone_294.svg deleted file mode 100644 index e6cdc57ec..000000000 --- a/doc-img/pantone_294.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_342.svg b/doc-img/pantone_342.svg deleted file mode 100644 index 69bcd9fba..000000000 --- a/doc-img/pantone_342.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_382.svg b/doc-img/pantone_382.svg deleted file mode 100644 index b7b2e29de..000000000 --- a/doc-img/pantone_382.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_469.svg b/doc-img/pantone_469.svg deleted file mode 100644 index d87075938..000000000 --- a/doc-img/pantone_469.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/pantone_medium_purple_c.svg b/doc-img/pantone_medium_purple_c.svg deleted file mode 100644 index 524589b04..000000000 --- a/doc-img/pantone_medium_purple_c.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 65bb95056..31d484c20 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -124,6 +124,8 @@ See the end of this file for a list of available jurisdictions and their codes. .ve, .dz, .gh, +.mg, +.ne, .am, .bd, .cn, @@ -156,6 +158,7 @@ See the end of this file for a list of available jurisdictions and their codes. .fr, .de, .gr, +.hr, .is, .ie, .it, @@ -165,6 +168,7 @@ See the end of this file for a list of available jurisdictions and their codes. .lv, .me, .nl, +.aw, .pl, .pt, .ro, diff --git a/doc-img/texas_orange.svg b/doc-img/texas_orange.svg deleted file mode 100644 index e4b96620b..000000000 --- a/doc-img/texas_orange.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/doc-img/white.svg b/doc-img/white.svg deleted file mode 100644 index c730ccb7c..000000000 --- a/doc-img/white.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/oneway.svg b/icons/oneway.svg deleted file mode 100644 index 476bf8103..000000000 --- a/icons/oneway.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/icons/oneway_black.svg b/icons/oneway_black.svg new file mode 100644 index 000000000..825ab153d --- /dev/null +++ b/icons/oneway_black.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/oneway_white.svg b/icons/oneway_white.svg new file mode 100644 index 000000000..70dc2aae4 --- /dev/null +++ b/icons/oneway_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/dot_city.svg b/icons/place_dot.svg similarity index 100% rename from icons/dot_city.svg rename to icons/place_dot.svg diff --git a/icons/place_dot_in_circle.svg b/icons/place_dot_in_circle.svg new file mode 100644 index 000000000..4b2703c0e --- /dev/null +++ b/icons/place_dot_in_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/star_state_capital.svg b/icons/place_star.svg similarity index 100% rename from icons/star_state_capital.svg rename to icons/place_star.svg diff --git a/icons/star_nation_capital.svg b/icons/place_star_in_circle.svg similarity index 100% rename from icons/star_nation_capital.svg rename to icons/place_star_in_circle.svg diff --git a/icons/poi_book_upright.svg b/icons/poi_book_upright.svg new file mode 100644 index 000000000..b13bdc7bc --- /dev/null +++ b/icons/poi_book_upright.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/icons/poi_car_repair.svg b/icons/poi_car_repair.svg new file mode 100644 index 000000000..042bff803 --- /dev/null +++ b/icons/poi_car_repair.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/poi_car_shop.svg b/icons/poi_car_shop.svg new file mode 100644 index 000000000..cfcf82ba7 --- /dev/null +++ b/icons/poi_car_shop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/poi_envelope.svg b/icons/poi_envelope.svg new file mode 100644 index 000000000..65113dabc --- /dev/null +++ b/icons/poi_envelope.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_fuel.svg b/icons/poi_fuel.svg new file mode 100644 index 000000000..319fc60b0 --- /dev/null +++ b/icons/poi_fuel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/poi_hostel.svg b/icons/poi_hostel.svg new file mode 100644 index 000000000..04d8bf388 --- /dev/null +++ b/icons/poi_hostel.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_hotel.svg b/icons/poi_hotel.svg new file mode 100644 index 000000000..576f5a1d7 --- /dev/null +++ b/icons/poi_hotel.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_mortarboard.svg b/icons/poi_mortarboard.svg new file mode 100644 index 000000000..3ff591076 --- /dev/null +++ b/icons/poi_mortarboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_police_shield.svg b/icons/poi_police_shield.svg new file mode 100644 index 000000000..d2507cf64 --- /dev/null +++ b/icons/poi_police_shield.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_buddhist.svg b/icons/poi_pow_buddhist.svg new file mode 100644 index 000000000..1151b63f2 --- /dev/null +++ b/icons/poi_pow_buddhist.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_christian.svg b/icons/poi_pow_christian.svg new file mode 100644 index 000000000..e259dc2dc --- /dev/null +++ b/icons/poi_pow_christian.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/poi_pow_generic.svg b/icons/poi_pow_generic.svg new file mode 100644 index 000000000..aa8709cde --- /dev/null +++ b/icons/poi_pow_generic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/poi_pow_hindu.svg b/icons/poi_pow_hindu.svg new file mode 100644 index 000000000..a804d367b --- /dev/null +++ b/icons/poi_pow_hindu.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_jewish.svg b/icons/poi_pow_jewish.svg new file mode 100644 index 000000000..b563611df --- /dev/null +++ b/icons/poi_pow_jewish.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_muslim.svg b/icons/poi_pow_muslim.svg new file mode 100644 index 000000000..47a67d798 --- /dev/null +++ b/icons/poi_pow_muslim.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_shinto.svg b/icons/poi_pow_shinto.svg new file mode 100644 index 000000000..4e2bf49f7 --- /dev/null +++ b/icons/poi_pow_shinto.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_sikh.svg b/icons/poi_pow_sikh.svg new file mode 100644 index 000000000..2b8d3957d --- /dev/null +++ b/icons/poi_pow_sikh.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_pow_taoist.svg b/icons/poi_pow_taoist.svg new file mode 100644 index 000000000..83b5b94fe --- /dev/null +++ b/icons/poi_pow_taoist.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/poi_supermarket.svg b/icons/poi_supermarket.svg new file mode 100644 index 000000000..561510454 --- /dev/null +++ b/icons/poi_supermarket.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_taxi.svg b/icons/poi_taxi.svg new file mode 100644 index 000000000..c8dcb2193 --- /dev/null +++ b/icons/poi_taxi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/shield40_us_nm_2.svg b/icons/shield40_us_nm_2.svg new file mode 100644 index 000000000..13a23c708 --- /dev/null +++ b/icons/shield40_us_nm_2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/shield40_us_nm_3.svg b/icons/shield40_us_nm_3.svg new file mode 100644 index 000000000..a15b60d6a --- /dev/null +++ b/icons/shield40_us_nm_3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/shield_badge_crossbar_2.svg b/icons/shield_badge_crossbar_2.svg new file mode 100644 index 000000000..be80b8769 --- /dev/null +++ b/icons/shield_badge_crossbar_2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_badge_crossbar_3.svg b/icons/shield_badge_crossbar_3.svg new file mode 100644 index 000000000..5cdda03ce --- /dev/null +++ b/icons/shield_badge_crossbar_3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_ca_mb_winnipeg.svg b/icons/shield_ca_mb_winnipeg.svg deleted file mode 100644 index 913652204..000000000 --- a/icons/shield_ca_mb_winnipeg.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/icons/shield_ca_ns_h.svg b/icons/shield_ca_ns_h.svg deleted file mode 100644 index 636f74df6..000000000 --- a/icons/shield_ca_ns_h.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/icons/shield_ca_ns_s_bdolsd.svg b/icons/shield_ca_ns_s_bdolsd.svg new file mode 100644 index 000000000..fddb5e67c --- /dev/null +++ b/icons/shield_ca_ns_s_bdolsd.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_cet.svg b/icons/shield_ca_ns_s_cet.svg new file mode 100644 index 000000000..f8523af35 --- /dev/null +++ b/icons/shield_ca_ns_s_cet.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_ct.svg b/icons/shield_ca_ns_s_ct.svg new file mode 100644 index 000000000..fa2c05e5b --- /dev/null +++ b/icons/shield_ca_ns_s_ct.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_dnisd.svg b/icons/shield_ca_ns_s_dnisd.svg new file mode 100644 index 000000000..b46081a94 --- /dev/null +++ b/icons/shield_ca_ns_s_dnisd.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_et.svg b/icons/shield_ca_ns_s_et.svg new file mode 100644 index 000000000..23ec82fc3 --- /dev/null +++ b/icons/shield_ca_ns_s_et.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_fdlt.svg b/icons/shield_ca_ns_s_fdlt.svg new file mode 100644 index 000000000..8f057fcc1 --- /dev/null +++ b/icons/shield_ca_ns_s_fdlt.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_gt.svg b/icons/shield_ca_ns_s_gt.svg new file mode 100644 index 000000000..b907a78cf --- /dev/null +++ b/icons/shield_ca_ns_s_gt.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_ksd.svg b/icons/shield_ca_ns_s_ksd.svg new file mode 100644 index 000000000..5270f0e63 --- /dev/null +++ b/icons/shield_ca_ns_s_ksd.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_lr.svg b/icons/shield_ca_ns_s_lr.svg new file mode 100644 index 000000000..a478d5625 --- /dev/null +++ b/icons/shield_ca_ns_s_lr.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_md.svg b/icons/shield_ca_ns_s_md.svg new file mode 100644 index 000000000..5e35b59b6 --- /dev/null +++ b/icons/shield_ca_ns_s_md.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_mdb.svg b/icons/shield_ca_ns_s_mdb.svg new file mode 100644 index 000000000..d4a9bf364 --- /dev/null +++ b/icons/shield_ca_ns_s_mdb.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_mkb.svg b/icons/shield_ca_ns_s_mkb.svg new file mode 100644 index 000000000..2a2f5d26e --- /dev/null +++ b/icons/shield_ca_ns_s_mkb.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_mt.svg b/icons/shield_ca_ns_s_mt.svg new file mode 100644 index 000000000..2c36efcf6 --- /dev/null +++ b/icons/shield_ca_ns_s_mt.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_ns_s_st.svg b/icons/shield_ca_ns_s_st.svg new file mode 100644 index 000000000..46ee7ebe1 --- /dev/null +++ b/icons/shield_ca_ns_s_st.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/icons/shield_ca_on_hamilton_blue.svg b/icons/shield_ca_on_hamilton_blue.svg new file mode 100644 index 000000000..eb3c79ac6 --- /dev/null +++ b/icons/shield_ca_on_hamilton_blue.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_ca_on_hamilton_green.svg b/icons/shield_ca_on_hamilton_green.svg new file mode 100644 index 000000000..43fe46665 --- /dev/null +++ b/icons/shield_ca_on_hamilton_green.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_ca_on_primary.svg b/icons/shield_ca_on_primary.svg index 71a72ce15..b6b25e117 100644 --- a/icons/shield_ca_on_primary.svg +++ b/icons/shield_ca_on_primary.svg @@ -1,4 +1,3 @@ - - + diff --git a/icons/shield_ca_on_toronto.svg b/icons/shield_ca_on_toronto.svg new file mode 100644 index 000000000..1a8f35a75 --- /dev/null +++ b/icons/shield_ca_on_toronto.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/shield_ca_tch.svg b/icons/shield_ca_tch.svg deleted file mode 100644 index 1aa807c16..000000000 --- a/icons/shield_ca_tch.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_ca_tch_2.svg b/icons/shield_ca_tch_2.svg new file mode 100644 index 000000000..063fe7777 --- /dev/null +++ b/icons/shield_ca_tch_2.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/shield_ca_tch_3.svg b/icons/shield_ca_tch_3.svg new file mode 100644 index 000000000..2deb71227 --- /dev/null +++ b/icons/shield_ca_tch_3.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/shield_in_nh_2.svg b/icons/shield_in_nh_2.svg index 1e9651039..8739bd0cb 100644 --- a/icons/shield_in_nh_2.svg +++ b/icons/shield_in_nh_2.svg @@ -1,3 +1,3 @@ - + diff --git a/icons/shield_in_nh_3.svg b/icons/shield_in_nh_3.svg index 8b3daed05..86b7ace5c 100644 --- a/icons/shield_in_nh_3.svg +++ b/icons/shield_in_nh_3.svg @@ -1,3 +1,3 @@ - - + + diff --git a/icons/shield_in_nh_4.svg b/icons/shield_in_nh_4.svg index 415f898da..4bd800d9f 100644 --- a/icons/shield_in_nh_4.svg +++ b/icons/shield_in_nh_4.svg @@ -1,3 +1,3 @@ - - + + diff --git a/icons/shield_kr_expressway_2.svg b/icons/shield_kr_expressway_2.svg index c74b8ea03..7bb5a8918 100644 --- a/icons/shield_kr_expressway_2.svg +++ b/icons/shield_kr_expressway_2.svg @@ -1,6 +1,4 @@ - - - - + + diff --git a/icons/shield_kr_expressway_3.svg b/icons/shield_kr_expressway_3.svg index b6c43ffe7..a2b1100c5 100644 --- a/icons/shield_kr_expressway_3.svg +++ b/icons/shield_kr_expressway_3.svg @@ -1,6 +1,4 @@ - - - - + + diff --git a/icons/shield_mx_cdmx_eje_central.svg b/icons/shield_mx_cdmx_eje_central.svg new file mode 100644 index 000000000..092346388 --- /dev/null +++ b/icons/shield_mx_cdmx_eje_central.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/shield_mx_cdmx_eje_nte.svg b/icons/shield_mx_cdmx_eje_nte.svg new file mode 100644 index 000000000..d4be2297f --- /dev/null +++ b/icons/shield_mx_cdmx_eje_nte.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_mx_cdmx_eje_ote.svg b/icons/shield_mx_cdmx_eje_ote.svg new file mode 100644 index 000000000..d11aa9d33 --- /dev/null +++ b/icons/shield_mx_cdmx_eje_ote.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_mx_cdmx_eje_pte.svg b/icons/shield_mx_cdmx_eje_pte.svg new file mode 100644 index 000000000..8eb70b599 --- /dev/null +++ b/icons/shield_mx_cdmx_eje_pte.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_mx_cdmx_eje_sur.svg b/icons/shield_mx_cdmx_eje_sur.svg new file mode 100644 index 000000000..617869491 --- /dev/null +++ b/icons/shield_mx_cdmx_eje_sur.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/shield_us_al_foley.svg b/icons/shield_us_al_foley.svg new file mode 100644 index 000000000..fe0a2c154 --- /dev/null +++ b/icons/shield_us_al_foley.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/icons/shield_us_az_scenic.svg b/icons/shield_us_az_scenic.svg new file mode 100644 index 000000000..d027e8acd --- /dev/null +++ b/icons/shield_us_az_scenic.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/icons/shield_us_fl_toll.svg b/icons/shield_us_fl_toll.svg index 1c83dbe7b..5c0d2ba74 100644 --- a/icons/shield_us_fl_toll.svg +++ b/icons/shield_us_fl_toll.svg @@ -1,7 +1,5 @@ - - - + + + - - diff --git a/icons/shield_us_in_jhmht.svg b/icons/shield_us_in_jhmht.svg new file mode 100644 index 000000000..af9bc36e1 --- /dev/null +++ b/icons/shield_us_in_jhmht.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/icons/shield_us_nh_turnpike.svg b/icons/shield_us_nh_turnpike.svg new file mode 100644 index 000000000..a612af8c1 --- /dev/null +++ b/icons/shield_us_nh_turnpike.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/icons/shield_us_oh_jhmht.svg b/icons/shield_us_oh_jhmht.svg new file mode 100644 index 000000000..189b99d38 --- /dev/null +++ b/icons/shield_us_oh_jhmht.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/icons/shield_us_orsb.svg b/icons/shield_us_orsb.svg new file mode 100644 index 000000000..16a013b2a --- /dev/null +++ b/icons/shield_us_orsb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/icons/shield_us_sd_csp.svg b/icons/shield_us_sd_csp.svg new file mode 100644 index 000000000..468c471f6 --- /dev/null +++ b/icons/shield_us_sd_csp.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/shield_us_ut_2.svg b/icons/shield_us_ut_2.svg index 598cb1678..b23fbf30e 100644 --- a/icons/shield_us_ut_2.svg +++ b/icons/shield_us_ut_2.svg @@ -1,4 +1,6 @@ - - + + + + diff --git a/icons/shield_us_ut_3.svg b/icons/shield_us_ut_3.svg index 946896860..7038d3967 100644 --- a/icons/shield_us_ut_3.svg +++ b/icons/shield_us_ut_3.svg @@ -1,4 +1,6 @@ - - - + + + + + diff --git a/package-lock.json b/package-lock.json index 1e45502b2..317229671 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1012 +1,1256 @@ { "name": "openstreetmap-americana", "version": "0.1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openstreetmap-americana", "version": "0.1.0", + "workspaces": [ + "shieldlib" + ], "dependencies": { - "color-namer": "^1.4.0", + "@americana/maplibre-shield-generator": "^0.0.5", "color-rgba": "^2.4.0", "events": "^3.3.0", - "fonteditor-core": "^2.1.11", + "fonteditor-core": "^2.4.0", "maplibre-gl": "^2.4.0", "openmapsamples": "github:adamfranco/OpenMapSamples", "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "tokenfield": "^1.5.2" }, "devDependencies": { - "@basemaps/sprites": "^6.32.1", - "@maplibre/maplibre-gl-style-spec": "^17.0.1", - "canvas": "^2.11.0", - "chai": "^4.3.7", - "commander": "^10.0.0", - "esbuild": "^0.17.4", - "glob": "^9.2.1", + "@basemaps/sprites": "^7.1.0", + "@mapbox/vector-tile": "^1.3.1", + "@maplibre/maplibre-gl-style-spec": "^17.1.0", + "@playwright/test": "^1.44.1", + "@types/chai": "^4.3.16", + "@types/color-namer": "^1.3.3", + "@types/mocha": "^10.0.6", + "@types/node": "^20.14.0", + "benchmark": "^2.1.4", + "canvas": "^2.11.2", + "chai": "^4.4.1", + "color-namer": "^1.4.0", + "commander": "^10.0.1", + "esbuild": "^0.17.19", + "glob": "^10.4.1", "google-font-installer": "^1.2.0", - "mocha": "^10.1.0", + "maplibre-gl": "^2.4.0", + "mocha": "^10.4.0", "npm-run-all": "^4.1.5", "open": "^8.4.2", - "prettier": "2.3.2", + "pbf": "^3.2.1", + "prettier": "^2.8.8", + "sharp": "^0.33.4", "shx": "^0.3.4", - "svgo": "^2.8.0" + "svgo": "^2.8.0", + "tsx": "^4.11.2", + "typescript": "^4.9.5" }, "engines": { - "node": ">=14", + "node": ">=18", "npm": ">=8.3.0" } }, + "node_modules/@americana/maplibre-shield-generator": { + "resolved": "shieldlib", + "link": true + }, "node_modules/@basemaps/sprites": { - "version": "6.32.1", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@basemaps/sprites/-/sprites-7.1.0.tgz", + "integrity": "sha512-IxEZxgBnIhUbe4RnG7FjKR4vEjI2oQaRoKWPDYUAkSPtU+dbcvQxe+HqUylyy0wmJILsf8fYz0A+ftcwNeePoQ==", "dev": true, - "license": "MIT", "dependencies": { "@mapbox/shelf-pack": "^3.2.0", - "@rushstack/ts-command-line": "^4.3.13", - "sharp": "^0.30.7" + "cmd-ts": "^0.12.1", + "sharp": "^0.33.0" }, "bin": { "basemaps-sprites": "bin/basemaps-sprites.mjs" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.17.11", + "node_modules/@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", "cpu": [ - "x64" + "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ - "linux" + "aix" ], "engines": { "node": ">=12" } }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, - "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/@mapbox/mapbox-gl-supported": { - "version": "2.0.1", - "license": "BSD-3-Clause" - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@mapbox/point-geometry": { - "version": "0.1.0", - "license": "ISC" - }, - "node_modules/@mapbox/shelf-pack": { - "version": "3.2.0", + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@mapbox/tiny-sdf": { - "version": "2.0.5", - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/vector-tile": { - "version": "1.3.1", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/point-geometry": "~0.1.0" + "node": ">=12" } }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "license": "ISC", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "17.0.1", + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.0", - "csscolorparser": "~1.0.2", - "json-stringify-pretty-compact": "^2.0.0", - "minimist": "^1.2.5", - "rw": "^1.3.3", - "sort-object": "^0.3.2" - }, - "bin": { - "gl-style-composite": "bin/gl-style-composite", - "gl-style-format": "bin/gl-style-format", - "gl-style-migrate": "bin/gl-style-migrate", - "gl-style-validate": "bin/gl-style-validate" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { - "version": "0.0.0", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/@rushstack/ts-command-line": { - "version": "4.11.0", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@types/argparse": "1.0.38", - "argparse": "~1.0.9", - "colors": "~1.2.1", - "string-argv": "~0.3.1" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@trysound/sax": { - "version": "0.2.0", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=10.13.0" + "node": ">=12" } }, - "node_modules/@types/argparse": { - "version": "1.0.38", + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" - }, - "node_modules/@types/geojson": { - "version": "7946.0.10", - "license": "MIT" - }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.2", - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.0", - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, - "node_modules/@types/pbf": { - "version": "3.0.2", - "license": "MIT" - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz", - "integrity": "sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.0.0" + "node": ">=12" } }, - "node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/agent-base": { - "version": "6.0.2", + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 6.0.0" + "node": ">=12" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/anymatch": { - "version": "3.1.3", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/argparse": { - "version": "1.0.10", + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/assertion-error": { - "version": "1.1.0", + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "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" - } + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" ], - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.2.0", "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/bl": { - "version": "4.1.0", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" } }, - "node_modules/braces": { - "version": "3.0.2", + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/buffer": { - "version": "5.7.1", + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], "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" - } + "optional": true, + "os": [ + "win32" ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "engines": { + "node": ">=12" } }, - "node_modules/call-bind": { - "version": "1.0.2", + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", + "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camel-case": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", - "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", - "dev": true, - "dependencies": { - "sentence-case": "^1.1.1", - "upper-case": "^1.1.1" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.2" } }, - "node_modules/camelcase": { - "version": "6.3.0", + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", + "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/canvas": { - "version": "2.11.0", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" + "url": "https://opencollective.com/libvips" }, - "engines": { - "node": ">=6" + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.2" } }, - "node_modules/canvas/node_modules/decompress-response": { - "version": "4.2.1", + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^2.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/canvas/node_modules/mimic-response": { - "version": "2.1.0", + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", + "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/libvips" } }, - "node_modules/canvas/node_modules/simple-get": { - "version": "3.1.1", + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", + "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/chai": { - "version": "4.3.7", + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", + "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/chalk": { - "version": "4.1.2", + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", + "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/libvips" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", + "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/check-error": { + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", + "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "*" + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/chokidar": { - "version": "3.5.3", + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", + "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } + "optional": true, + "os": [ + "linux" ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, "engines": { - "node": ">= 8.10.0" + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "dev": true, - "license": "ISC" - }, - "node_modules/chroma-js": { - "version": "1.4.1" - }, - "node_modules/cliui": { - "version": "7.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/color": { - "version": "4.2.3", + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", + "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-convert/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/color-namer": { - "version": "1.4.0", - "license": "MIT", - "dependencies": { - "chroma-js": "^1.3.4", - "es6-weak-map": "^2.0.3" - } - }, - "node_modules/color-parse": { - "version": "1.4.2", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0" - } - }, - "node_modules/color-parse/node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/color-rgba": { - "version": "2.4.0", - "license": "MIT", - "dependencies": { - "color-parse": "^1.4.2", - "color-space": "^2.0.0" + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.2" } }, - "node_modules/color-space": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", + "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.2" } }, - "node_modules/color-string/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", + "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.31", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.2" } }, - "node_modules/colors": { - "version": "1.2.5", + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", + "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.1.90" + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.2" } }, - "node_modules/commander": { - "version": "10.0.0", + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", + "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "dev": true, - "license": "ISC" - }, - "node_modules/copy-paste-win32fix": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/copy-paste-win32fix/-/copy-paste-win32fix-1.4.0.tgz", - "integrity": "sha512-covImiUCOAvAPzWHWwrcEJYOPZVdAvxp5PBbSdtlK1xt2O5IJsBOw+1cQTLhP2L4ph4syKfbDFzKRlNGyjbBSw==", + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", + "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "iconv-lite": "^0.4.8" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.2" } }, - "node_modules/cross-spawn": { - "version": "6.0.5", + "node_modules/@img/sharp-wasm32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", + "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", + "cpu": [ + "wasm32" + ], "dev": true, - "license": "MIT", + "optional": true, "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "@emnapi/runtime": "^1.1.1" }, "engines": { - "node": ">=4.8" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.1", + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", + "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/css-select": { - "version": "4.2.1", + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", + "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "url": "https://opencollective.com/libvips" } }, - "node_modules/css-tree": { - "version": "1.1.3", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "MIT", "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12" } }, - "node_modules/css-what": { - "version": "5.1.0", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "license": "BSD-2-Clause", "engines": { - "node": ">= 6" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/csscolorparser": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/csso": { - "version": "4.2.0", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", "dependencies": { - "css-tree": "^1.1.2" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/d": { - "version": "1.0.1", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/debug": { - "version": "4.3.4", - "dev": true, - "license": "MIT", + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", "dependencies": { - "ms": "2.1.2" + "get-stream": "^6.0.1", + "minimist": "^1.2.6" }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", + "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==" }, - "node_modules/decamelize": { - "version": "4.0.0", + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/decompress-response": { - "version": "6.0.0", + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/shelf-pack": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/shelf-pack/-/shelf-pack-3.2.0.tgz", + "integrity": "sha512-dyQxe6ukILV6qaEvxoKCIwhblgRjYp1ZGlClo4xvfbmxzFO5LYu7Tnrg2AZrRgN7VsSragsGcNjzUe9kCdKHYQ==", "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.0.0" } }, - "node_modules/deep-eql": { - "version": "4.1.2", - "dev": true, - "license": "MIT", + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "dev": true + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", "dependencies": { - "type-detect": "^4.0.0" - }, + "@mapbox/point-geometry": "~0.1.0" + } + }, + "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==", "engines": { - "node": ">=6" + "node": ">=6.0.0" } }, - "node_modules/deep-extend": { - "version": "0.6.0", + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-17.1.0.tgz", + "integrity": "sha512-ie1GI7ceByOJ9u/xoqx9IMkFMo8FcvEzEQhx+GANviF7ARE4OhB8FBm17YYVPtMSRg3zE6J1Yxy3n97/d4HahA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.0", + "csscolorparser": "~1.0.2", + "json-stringify-pretty-compact": "^2.0.0", + "minimist": "^1.2.5", + "rw": "^1.3.3", + "sort-object": "^0.3.2" + }, + "bin": { + "gl-style-format": "bin/gl-style-format.js", + "gl-style-migrate": "bin/gl-style-migrate.js", + "gl-style-validate": "bin/gl-style-validate.js" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "license": "MIT", + "optional": true, "engines": { - "node": ">=8" + "node": ">=14" } }, - "node_modules/define-properties": { - "version": "1.1.3", + "node_modules/@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", "dev": true, - "license": "MIT", "dependencies": { - "object-keys": "^1.0.12" + "playwright": "1.44.1" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=16" } }, - "node_modules/delegates": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.0.1", + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, - "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/diff": { - "version": "5.0.0", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@types/chai": { + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", + "dev": true + }, + "node_modules/@types/color-namer": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/color-namer/-/color-namer-1.3.3.tgz", + "integrity": "sha512-DJ0MHHqazwb94dUBRhahK49nXhRGk2DIRNrVNVR3U3uQjYWY4uNhEjHXO+TbFQGpZcwli1pCj6xm3S3KsB/+3A==", + "dev": true + }, + "node_modules/@types/color-rgba": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/color-rgba/-/color-rgba-2.1.2.tgz", + "integrity": "sha512-gDV/fgs4Mpc+hcHygYnM2EDgcxaHmvIGrAVxZJjP38f2IXQKHiGf0XMHhFd+dz8EVPSNTwHL5DJ6yXsxEiCQkg==", + "dev": true + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.0.tgz", + "integrity": "sha512-5cHBxFGJx6L4s56Bubp4fglrEpmyJypsqI6RgzMfBHWUJQGWAAi8cWcgetEbZXHYXo9C2Fa4EEds/uSyS4cxmA==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "engines": { - "node": ">=0.3.1" + "node": ">=10.0.0" } }, - "node_modules/dom-serializer": { - "version": "1.3.2", + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "license": "MIT", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "debug": "4" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": ">= 6.0.0" } }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "engines": { + "node": ">=6" } }, - "node_modules/domelementtype": { - "version": "2.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } }, - "node_modules/domhandler": { + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, + "node_modules/ansi-styles": { "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "domelementtype": "^2.2.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 4" + "node": ">=8" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/domutils": { - "version": "2.8.0", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "engines": { + "node": ">= 8" } }, - "node_modules/earcut": { - "version": "2.2.4", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true }, - "node_modules/end-of-stream": { - "version": "1.4.4", + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "MIT", "dependencies": { - "once": "^1.4.0" + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/es-abstract": { - "version": "1.19.1", + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -1015,14 +1259,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, - "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1031,430 +1281,520 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.62", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" + "node": "*" } }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true }, - "node_modules/esbuild": { - "version": "0.17.11", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.11", - "@esbuild/android-arm64": "0.17.11", - "@esbuild/android-x64": "0.17.11", - "@esbuild/darwin-arm64": "0.17.11", - "@esbuild/darwin-x64": "0.17.11", - "@esbuild/freebsd-arm64": "0.17.11", - "@esbuild/freebsd-x64": "0.17.11", - "@esbuild/linux-arm": "0.17.11", - "@esbuild/linux-arm64": "0.17.11", - "@esbuild/linux-ia32": "0.17.11", - "@esbuild/linux-loong64": "0.17.11", - "@esbuild/linux-mips64el": "0.17.11", - "@esbuild/linux-ppc64": "0.17.11", - "@esbuild/linux-riscv64": "0.17.11", - "@esbuild/linux-s390x": "0.17.11", - "@esbuild/linux-x64": "0.17.11", - "@esbuild/netbsd-x64": "0.17.11", - "@esbuild/openbsd-x64": "0.17.11", - "@esbuild/sunos-x64": "0.17.11", - "@esbuild/win32-arm64": "0.17.11", - "@esbuild/win32-ia32": "0.17.11", - "@esbuild/win32-x64": "0.17.11" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", + "node_modules/benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" + "dependencies": { + "lodash": "^4.17.4", + "platform": "^1.3.3" } }, - "node_modules/events": { - "version": "3.3.0", - "license": "MIT", + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { - "node": ">=0.8.x" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "dev": true, - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true }, - "node_modules/ext": { - "version": "1.7.0", - "license": "ISC", + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "license": "ISC" - }, - "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "balanced-match": "^1.0.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", + "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==", "dependencies": { - "to-regex-range": "^5.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, - "node_modules/find-up": { - "version": "5.0.0", + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, - "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flat": { - "version": "5.0.2", + "node_modules/camel-case": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", + "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/fonteditor-core": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/fonteditor-core/-/fonteditor-core-2.1.11.tgz", - "integrity": "sha512-zgO5tHw5vzqYqkeb9jthIANbOOGwWqNWovINCMyIDLh9xmujsHPqloWJdk0PMbJHbZmiLG1F/kygfuWS8+TDfA==", "dependencies": { - "@xmldom/xmldom": "^0.8.3" + "sentence-case": "^1.1.1", + "upper-case": "^1.1.1" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "dev": true, - "license": "MIT" + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/fs-minipass": { - "version": "2.1.0", + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", "dev": true, - "license": "ISC", + "hasInstallScript": true, "dependencies": { - "minipass": "^3.0.0" + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" }, "engines": { - "node": ">= 8" + "node": ">=6" } }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, - "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/gauge": { - "version": "3.0.2", - "dev": true, - "license": "ISC", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/geojson-validation": { - "version": "1.0.2", - "license": "LGPL-3", - "bin": { - "gjv": "bin/gjv" + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" } }, - "node_modules/geojson-vt": { - "version": "3.2.1", - "license": "ISC" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "dev": true, - "license": "ISC", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/get-func-name": { + "node_modules/chownr": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, - "license": "MIT", "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "dev": true, - "license": "MIT", + "node_modules/chroma-js": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz", + "integrity": "sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "license": "MIT", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", + "node_modules/cmd-ts": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/cmd-ts/-/cmd-ts-0.12.1.tgz", + "integrity": "sha512-k93f6LgFEzx7/vBy5qV+tu1VI8YuH0DZmwNXPvRaDR4Qp7Fl9+pUSylX/CKHrWoKqHMJjNOLb1NeYTvHOjQ2Kw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "chalk": "^4.0.0", + "debug": "^4.3.4", + "didyoumean": "^1.2.2", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/github-from-package": { - "version": "0.0.0", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-namer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/color-namer/-/color-namer-1.4.0.tgz", + "integrity": "sha512-3mQMY9MJyfdV2uhe+xjQWcKHtYnPtl5svGjt89V2WWT2MlaLAd7C02886Wq7H1MTjjIIEa/NJLYPNF/Lhxhq2A==", "dev": true, - "license": "MIT" + "dependencies": { + "chroma-js": "^1.3.4", + "es6-weak-map": "^2.0.3" + } }, - "node_modules/gl-matrix": { - "version": "3.4.3", - "license": "MIT" + "node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "dependencies": { + "color-name": "^1.0.0" + } }, - "node_modules/glob": { - "version": "9.2.1", + "node_modules/color-rgba": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.4.0.tgz", + "integrity": "sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==", + "dependencies": { + "color-parse": "^1.4.2", + "color-space": "^2.0.0" + } + }, + "node_modules/color-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.0.1.tgz", + "integrity": "sha512-nKqUYlo0vZATVOFHY810BSYjmCARrG7e5R3UE3CQlyjJTvv5kSSmPG1kzm/oDyyqjehM+lW1RnEt9It9GNa5JA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dev": true, - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^7.4.1", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.1.90" } }, - "node_modules/glob-parent": { - "version": "5.1.2", + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=14" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/copy-paste-win32fix": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/copy-paste-win32fix/-/copy-paste-win32fix-1.4.0.tgz", + "integrity": "sha512-covImiUCOAvAPzWHWwrcEJYOPZVdAvxp5PBbSdtlK1xt2O5IJsBOw+1cQTLhP2L4ph4syKfbDFzKRlNGyjbBSw==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "iconv-lite": "^0.4.8" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "7.4.2", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/global-prefix": { - "version": "3.0.0", - "license": "MIT", + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "mdn-data": "2.0.14", + "source-map": "^0.6.1" }, "engines": { - "node": ">=6" + "node": ">=8.0.0" } }, - "node_modules/google-font-installer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/google-font-installer/-/google-font-installer-1.2.0.tgz", - "integrity": "sha512-0tgwTRphnjiTEUflczlgJU0PVao1Y/oA0ZPYH3d4Twc7Uc8SL36mQyjzxY8KXaVUjID++XDFrGo3aJVNDodE3Q==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "dependencies": { - "async": "^1.5.2", - "colors": "^1.1.2", - "commander": "^2.9.0", - "copy-paste-win32fix": "^1.4.0", - "file-type": "^3.6.0", - "mv": "^2.1.1", - "pascal-case": "^1.1.2" - }, - "bin": { - "gfi": "cli.js" + "engines": { + "node": ">= 6" }, - "optionalDependencies": { - "node-powershell": "^4.0.0" + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/google-font-installer/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" }, - "node_modules/graceful-fs": { - "version": "4.2.10", + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", "dev": true, - "license": "ISC" + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } }, - "node_modules/has": { - "version": "1.0.3", + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", "dev": true, - "license": "MIT", "dependencies": { - "function-bind": "^1.1.1" + "es5-ext": "^0.10.64", + "type": "^2.7.2" }, "engines": { - "node": ">= 0.4.0" + "node": ">=0.12" } }, - "node_modules/has-bigints": { + "node_modules/data-view-buffer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, - "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, - "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, "engines": { "node": ">= 0.4" }, @@ -1462,12 +1802,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { + "node_modules/data-view-byte-offset": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, - "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -1476,175 +1819,272 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, - "license": "ISC" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, - "node_modules/he": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", "dev": true, - "license": "ISC" + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, - "license": "MIT", "dependencies": { - "agent-base": "6", - "debug": "4" + "type-detect": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ieee754": { + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { "version": "1.2.1", - "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": "BSD-3-Clause" - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "dev": true, - "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/interpret": { - "version": "1.4.0", + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true }, - "node_modules/is-bigint": { - "version": "1.0.4", + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, - "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, - "node_modules/is-boolean-object": { - "version": "1.1.2", + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "domelementtype": "^2.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/is-callable": { - "version": "1.2.4", + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/is-core-module": { - "version": "2.9.0", + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/is-date-object": { - "version": "1.0.5", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -1653,51 +2093,63 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" + "dependencies": { + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/is-extglob": { - "version": "2.1.1", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, - "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/is-glob": { - "version": "4.0.3", + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, - "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, - "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, "engines": { "node": ">= 0.4" }, @@ -1705,91 +2157,107 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.6", + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, - "license": "MIT", + "hasInstallScript": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, - "node_modules/is-regex": { - "version": "1.1.4", + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "d": "^1.0.2", + "ext": "^1.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.1", + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" } }, - "node_modules/is-string": { - "version": "1.0.7", + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "dev": true, - "license": "MIT", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "engines": { "node": ">=10" }, @@ -1797,101 +2265,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakref": { - "version": "1.0.2", + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10" } }, - "node_modules/is-wsl": { - "version": "2.2.0", + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", "dev": true, - "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" + "d": "1", + "es5-ext": "~0.10.14" } }, - "node_modules/isexe": { - "version": "2.0.0", - "license": "ISC" + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } }, - "node_modules/js-yaml": { - "version": "4.1.0", + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, - "license": "MIT", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "type": "^2.7.2" } }, - "node_modules/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-pretty-compact": { - "version": "2.0.0", + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", "dev": true, - "license": "MIT" - }, - "node_modules/kdbush": { - "version": "3.0.0", - "license": "ISC" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT", + "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==", "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dependencies": { - "p-locate": "^5.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=10" @@ -1900,227 +2342,225 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" } }, - "node_modules/loupe": { - "version": "2.3.6", - "dev": true, - "license": "MIT", + "node_modules/fonteditor-core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fonteditor-core/-/fonteditor-core-2.4.0.tgz", + "integrity": "sha512-c8o4EKdcTJXNtYeoiJ7Y6pNDM5fTpeAneEz4lV4E8YrTe7liqrOyNeLI8SPxY0RR0S0nSpHcop1Wyoz4AlYTuQ==", "dependencies": { - "get-func-name": "^2.0.0" + "@xmldom/xmldom": "^0.8.3" } }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "license": "ISC", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "is-callable": "^1.1.3" } }, - "node_modules/make-dir": { - "version": "3.1.0", + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, - "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/maplibre-gl": { - "version": "2.4.0", - "hasInstallScript": true, - "license": "BSD-3-Clause", + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^2.0.1", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.5", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "@types/geojson": "^7946.0.10", - "@types/mapbox__point-geometry": "^0.1.2", - "@types/mapbox__vector-tile": "^1.3.0", - "@types/pbf": "^3.0.2", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.4", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.4.3", - "global-prefix": "^3.0.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.2", - "quickselect": "^2.0.0", - "supercluster": "^7.1.5", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.3" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/mdn-data": { - "version": "2.0.14", - "dev": true, - "license": "CC0-1.0" + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/memorystream": { - "version": "0.3.1", - "dev": true, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.10.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/mimic-response": { - "version": "3.1.0", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimatch": { - "version": "3.1.1", + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimist": { - "version": "1.2.6", - "license": "MIT" - }, - "node_modules/minipass": { - "version": "4.2.4", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minizlib": { - "version": "2.1.2", + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", + "node_modules/geojson-validation": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/geojson-validation/-/geojson-validation-1.0.2.tgz", + "integrity": "sha512-K5jrJ4wFvORn2pRKeg181LL0QPYuEKn2KHPvfH1m2QtFlAXFLKdseqt0XwBM3ELOY7kNM1fglRQ6ZwUQZ5S00A==", "bin": { - "mkdirp": "bin/cmd.js" - }, + "gjv": "bin/gjv" + } + }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "engines": { - "node": ">=10" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, - "license": "MIT" + "engines": { + "node": "*" + } }, - "node_modules/mocha": { - "version": "10.1.0", + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { - "node": ">= 14.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "engines": { "node": ">=10" }, @@ -2128,446 +2568,553 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/get-tsconfig": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "resolve-pkg-maps": "^1.0.0" }, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "node_modules/glob": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "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==", "dependencies": { - "balanced-match": "^1.0.0" + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=6" } }, - "node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/murmurhash-js": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "dev": true, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dependencies": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" + "isexe": "^2.0.0" }, - "engines": { - "node": ">=0.8.0" + "bin": { + "which": "bin/which" } }, - "node_modules/mv/node_modules/glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mv/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/google-font-installer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/google-font-installer/-/google-font-installer-1.2.0.tgz", + "integrity": "sha512-0tgwTRphnjiTEUflczlgJU0PVao1Y/oA0ZPYH3d4Twc7Uc8SL36mQyjzxY8KXaVUjID++XDFrGo3aJVNDodE3Q==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "async": "^1.5.2", + "colors": "^1.1.2", + "commander": "^2.9.0", + "copy-paste-win32fix": "^1.4.0", + "file-type": "^3.6.0", + "mv": "^2.1.1", + "pascal-case": "^1.1.2" }, "bin": { - "mkdirp": "bin/cmd.js" + "gfi": "cli.js" + }, + "optionalDependencies": { + "node-powershell": "^4.0.0" } }, - "node_modules/mv/node_modules/rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "node_modules/google-font-installer/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "glob": "^6.0.1" + "get-intrinsic": "^1.1.3" }, - "bin": { - "rimraf": "bin.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nan": { - "version": "2.17.0", - "dev": true, - "license": "MIT" + "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 }, - "node_modules/nanoid": { - "version": "3.3.3", + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=8" } }, - "node_modules/napi-build-utils": { + "node_modules/has-property-descriptors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "license": "MIT" + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, - "bin": { - "ncp": "bin/ncp" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "license": "ISC" - }, - "node_modules/nice-try": { - "version": "1.0.5", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "license": "MIT" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/node-abi": { - "version": "3.33.0", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "license": "MIT", "dependencies": { - "semver": "^7.3.5" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "dev": true, - "license": "MIT" + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true }, - "node_modules/node-fetch": { - "version": "2.6.9", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, - "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "function-bind": "^1.1.2" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/node-powershell": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/node-powershell/-/node-powershell-4.0.0.tgz", - "integrity": "sha512-WCZMLgwkjW9G/DZsZwyCEAXhMMzShLRUlnYS+EETRqRLSdUMbuO4xiQxIOeAutwQgvj75NvC58CorHFlx0olIA==", - "dev": true, - "optional": true, - "dependencies": { - "chalk": "^2.4.1", - "shortid": "^2.2.14" + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" } }, - "node_modules/node-powershell/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "optional": true, "dependencies": { - "color-convert": "^1.9.0" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "node_modules/node-powershell/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "optional": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/node-powershell/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, - "optional": true, "dependencies": { - "color-name": "1.1.3" + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/node-powershell/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true, - "optional": true, "engines": { - "node": ">=4" + "node": ">= 0.10" } }, - "node_modules/node-powershell/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, - "optional": true, "dependencies": { - "has-flag": "^3.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nopt": { - "version": "5.0.0", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, - "license": "ISC", "dependencies": { - "abbrev": "1" + "has-bigints": "^1.0.1" }, - "bin": { - "nopt": "bin/nopt.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/normalize-path": { - "version": "3.0.0", + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" + "is-typed-array": "^1.1.13" }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, - "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/has-flag": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", + "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==", "dependencies": { - "has-flag": "^3.0.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "node": ">=0.10.0" } }, - "node_modules/nth-check": { - "version": "2.0.1", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "dev": true, - "license": "MIT", + "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==", "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" } }, - "node_modules/object-inspect": { - "version": "1.12.0", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, - "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object.assign": { - "version": "4.1.2", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2576,68 +3123,70 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/once": { - "version": "1.4.0", + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, - "license": "ISC", "dependencies": { - "wrappy": "1" - } - }, - "node_modules/open": { - "version": "8.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "call-bind": "^1.0.7" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openmapsamples": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/adamfranco/OpenMapSamples.git#5014e9f74d7fd49b4e57bc2e8045d3e0b913f221", - "license": "LGPL-3.0-or-later", - "dependencies": { - "geojson-validation": "^1.0.2" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/openmapsamples-maplibre": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/adamfranco/OpenMapSamples-MapLibre.git#d0bb7b897c95bd0ef6adcd4e3a2ad409b9266c27", - "license": "LGPL-3.0-or-later", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "dependencies": { - "maplibre-gl": "^2.0", - "openmapsamples": "github:adamfranco/OpenMapSamples" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-limit": { - "version": "3.1.0", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, - "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-locate": { - "version": "5.0.0", + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, - "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "engines": { "node": ">=10" }, @@ -2645,975 +3194,896 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pascal-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", - "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "camel-case": "^1.1.1", - "upper-case-first": "^1.1.0" + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-exists": { - "version": "4.0.0", + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, - "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, - "node_modules/path-scurry": { - "version": "1.6.1", + "node_modules/jackspeak": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" + "@isaacs/cliui": "^8.0.2" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "7.18.3", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/pbf": { - "version": "3.2.1", - "license": "BSD-3-Clause", + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" + "argparse": "^2.0.1" }, "bin": { - "pbf": "bin/pbf" + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "node_modules/json-stringify-pretty-compact": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", + "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==", + "dev": true }, - "node_modules/pidtree": { - "version": "0.3.1", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true }, - "node_modules/pify": { + "node_modules/kdbush": { "version": "3.0.0", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/potpack": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/prebuild-install": { - "version": "7.1.1", + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, - "license": "MIT", "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "license": "MIT" + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "dev": true, - "license": "MIT", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/quickselect": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/randombytes": { - "version": "2.1.0", + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, - "license": "MIT", "dependencies": { - "safe-buffer": "^5.1.0" + "get-func-name": "^2.0.1" } }, - "node_modules/rc": { - "version": "1.2.8", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true }, - "node_modules/read-pkg": { - "version": "3.0.0", + "node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": "14 || >=16.14" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true }, - "node_modules/readable-stream": { - "version": "3.6.1", + "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": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "semver": "^6.0.0" }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/readdirp": { - "version": "3.6.0", + "node_modules/make-dir/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": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "dev": true, + "node_modules/maplibre-gl": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-2.4.0.tgz", + "integrity": "sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==", + "hasInstallScript": true, "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^2.0.1", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.5", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@types/geojson": "^7946.0.10", + "@types/mapbox__point-geometry": "^0.1.2", + "@types/mapbox__vector-tile": "^1.3.0", + "@types/pbf": "^3.0.2", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "global-prefix": "^3.0.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.2", + "quickselect": "^2.0.0", + "supercluster": "^7.1.5", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "node_modules/maplibre-gl/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==" }, - "node_modules/resolve": { - "version": "1.22.0", + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, "bin": { - "resolve": "bin/resolve" + "marked": "bin/marked.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 12" } }, - "node_modules/resolve-protobuf-schema": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "protocol-buffers-schema": "^3.3.1" + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", + "node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rw": { - "version": "1.3.3", - "dev": true, - "license": "BSD-3-Clause" + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/safe-buffer": { - "version": "5.2.1", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "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" + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "node_modules/safer-buffer": { + "node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.3.7", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, - "license": "ISC", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "minipass": "^3.0.0", + "yallist": "^4.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/sentence-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", - "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", - "dev": true, - "dependencies": { - "lower-case": "^1.1.1" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "node": ">= 8" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.30.7", + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=12.13.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=8" } }, - "node_modules/shebang-command": { - "version": "1.2.0", + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "minimist": "^1.2.6" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "dev": true, - "license": "MIT", + "node_modules/mocha": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "8.1.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 14.0.0" } }, - "node_modules/shell-quote": { - "version": "1.7.3", - "dev": true, - "license": "MIT" - }, - "node_modules/shelljs": { - "version": "0.8.5", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/mocha/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" + "ms": "2.1.2" }, "engines": { - "node": ">=4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/shortid": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", - "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", - "dev": true, - "optional": true, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dependencies": { - "nanoid": "^2.1.0" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "node_modules/shortid/node_modules/nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", - "dev": true, - "optional": true + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/shx": { - "version": "0.3.4", - "dev": true, - "license": "MIT", + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, - "node_modules/simple-concat": { - "version": "1.0.1", - "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/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, - "node_modules/simple-get": { - "version": "4.0.1", + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", "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": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", + "node_modules/mv/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { - "is-arrayish": "^0.3.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "dev": true, - "license": "MIT" - }, - "node_modules/sort-asc": { - "version": "0.1.0", + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/sort-desc": { - "version": "0.1.1", + "node_modules/mv/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/sort-object": { - "version": "0.3.2", + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { - "sort-asc": "^0.1.0", - "sort-desc": "^0.1.1" + "glob": "^6.0.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/source-map": { - "version": "0.6.1", + "node_modules/nan": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "dev": true + }, + "node_modules/nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } + "optional": true }, - "node_modules/spdx-correct": { - "version": "3.1.1", + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "bin": { + "ncp": "bin/ncp" } }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "dev": true, - "license": "CC-BY-3.0" + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, - "license": "MIT", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", + "node_modules/node-powershell": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/node-powershell/-/node-powershell-4.0.0.tgz", + "integrity": "sha512-WCZMLgwkjW9G/DZsZwyCEAXhMMzShLRUlnYS+EETRqRLSdUMbuO4xiQxIOeAutwQgvj75NvC58CorHFlx0olIA==", "dev": true, - "license": "CC0-1.0" + "optional": true, + "dependencies": { + "chalk": "^2.4.1", + "shortid": "^2.2.14" + } }, - "node_modules/sprintf-js": { - "version": "1.0.3", + "node_modules/node-powershell/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "BSD-3-Clause" + "optional": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/stable": { - "version": "0.1.8", + "node_modules/node-powershell/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT" + "optional": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/string_decoder": { - "version": "1.1.1", + "node_modules/node-powershell/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", + "optional": true, "dependencies": { - "safe-buffer": "~5.1.0" + "color-name": "1.1.3" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", + "node_modules/node-powershell/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, - "license": "MIT" + "optional": true }, - "node_modules/string-argv": { - "version": "0.3.1", + "node_modules/node-powershell/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", + "optional": true, "engines": { - "node": ">=0.6.19" + "node": ">=0.8.0" } }, - "node_modules/string-width": { - "version": "4.2.3", + "node_modules/node-powershell/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "optional": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.3", + "node_modules/node-powershell/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", + "optional": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "abbrev": "1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "bin": { + "nopt": "bin/nopt.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/strip-bom": { - "version": "3.0.0", + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" + "bin": { + "semver": "bin/semver" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "dev": true, - "license": "MIT", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "engines": { "node": ">=0.10.0" } }, - "node_modules/supercluster": { - "version": "7.1.5", - "license": "ISC", - "dependencies": { - "kdbush": "^3.0.0" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, - "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" }, - "engines": { - "node": ">=10" + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 4" } }, - "node_modules/svgo": { - "version": "2.8.0", + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=4" } }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", + "node_modules/npm-run-all/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/tar": { - "version": "6.1.13", + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "ISC", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/tar-fs": { - "version": "2.1.1", + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "color-name": "1.1.3" } }, - "node_modules/tar-stream": { - "version": "2.2.0", + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, - "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">=6" + "node": ">=4.8" } }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", + "node_modules/npm-run-all/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": "ISC", "engines": { - "node": ">=10" + "node": ">=0.8.0" } }, - "node_modules/tinyqueue": { - "version": "2.0.3", - "license": "ISC" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, "engines": { - "node": ">=8.0" + "node": ">=4" } }, - "node_modules/tokenfield": { - "version": "1.5.2", - "license": "BSD-3-Clause" - }, - "node_modules/tr46": { - "version": "0.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "safe-buffer": "^5.0.1" + "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, - "node_modules/type": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/type-detect": { - "version": "4.0.8", + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/unbox-primitive": { - "version": "1.0.1", + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "semver": "bin/semver" } }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", - "dev": true - }, - "node_modules/upper-case-first": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", - "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "dependencies": { - "upper-case": "^1.1.1" + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/which": { + "node_modules/npm-run-all/node_modules/which": { "version": "1.3.1", - "license": "ISC", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3621,111 +4091,137 @@ "which": "bin/which" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" } }, - "node_modules/wide-align": { - "version": "1.1.5", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, - "license": "ISC", "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/workerpool": { - "version": "6.2.1", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, - "license": "Apache-2.0" + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrappy": { - "version": "1.0.2", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "ISC" + "engines": { + "node": ">= 0.4" + } }, - "node_modules/y18n": { - "version": "5.0.8", + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, - "license": "ISC", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } }, - "node_modules/yargs": { - "version": "16.2.0", + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dev": true, - "license": "MIT", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" + "node_modules/openmapsamples": { + "version": "0.1.0", + "resolved": "git+ssh://git@github.com/adamfranco/OpenMapSamples.git#5014e9f74d7fd49b4e57bc2e8045d3e0b913f221", + "dependencies": { + "geojson-validation": "^1.0.2" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "dev": true, - "license": "MIT", + "node_modules/openmapsamples-maplibre": { + "version": "0.1.0", + "resolved": "git+ssh://git@github.com/adamfranco/OpenMapSamples-MapLibre.git#d0bb7b897c95bd0ef6adcd4e3a2ad409b9266c27", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "maplibre-gl": "^2.0", + "openmapsamples": "github:adamfranco/OpenMapSamples" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { "node": ">=10" }, @@ -3733,2423 +4229,2012 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "shieldlib": { - "extraneous": true - } - }, - "dependencies": { - "@basemaps/sprites": { - "version": "6.32.1", + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, - "requires": { - "@mapbox/shelf-pack": "^3.2.0", - "@rushstack/ts-command-line": "^4.3.13", - "sharp": "^0.30.7" + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" } }, - "@esbuild/linux-x64": { - "version": "0.17.11", + "node_modules/pascal-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", + "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", "dev": true, - "optional": true - }, - "@mapbox/geojson-rewind": { - "version": "0.5.2", - "requires": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" + "dependencies": { + "camel-case": "^1.1.1", + "upper-case-first": "^1.1.0" } }, - "@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2" - }, - "@mapbox/mapbox-gl-supported": { - "version": "2.0.1" + "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==", + "engines": { + "node": ">=8" + } }, - "@mapbox/node-pre-gyp": { - "version": "1.0.10", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "requires": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" + "engines": { + "node": ">=0.10.0" } }, - "@mapbox/point-geometry": { - "version": "0.1.0" + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "@mapbox/shelf-pack": { - "version": "3.2.0", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "@mapbox/tiny-sdf": { - "version": "2.0.5" - }, - "@mapbox/unitbezier": { - "version": "0.0.1" - }, - "@mapbox/vector-tile": { - "version": "1.3.1", - "requires": { - "@mapbox/point-geometry": "~0.1.0" + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "@mapbox/whoots-js": { - "version": "3.1.0" - }, - "@maplibre/maplibre-gl-style-spec": { - "version": "17.0.1", + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, - "requires": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.0", - "csscolorparser": "~1.0.2", - "json-stringify-pretty-compact": "^2.0.0", - "minimist": "^1.2.5", - "rw": "^1.3.3", - "sort-object": "^0.3.2" - }, "dependencies": { - "@mapbox/unitbezier": { - "version": "0.0.0", - "dev": true - } + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@rushstack/ts-command-line": { - "version": "4.11.0", + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, - "requires": { - "@types/argparse": "1.0.38", - "argparse": "~1.0.9", - "colors": "~1.2.1", - "string-argv": "~0.3.1" + "engines": { + "node": "*" } }, - "@trysound/sax": { - "version": "0.2.0", - "dev": true + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } }, - "@types/argparse": { - "version": "1.0.38", + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, - "@types/geojson": { - "version": "7946.0.10" - }, - "@types/mapbox__point-geometry": { - "version": "0.1.2" - }, - "@types/mapbox__vector-tile": { - "version": "1.3.0", - "requires": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "@types/pbf": { - "version": "3.0.2" - }, - "@xmldom/xmldom": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz", - "integrity": "sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==" - }, - "abbrev": { - "version": "1.1.1", - "dev": true - }, - "agent-base": { - "version": "6.0.2", + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true, - "requires": { - "debug": "4" + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" } }, - "ansi-colors": { - "version": "4.1.1", - "dev": true + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "ansi-regex": { - "version": "5.0.1", + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", "dev": true }, - "ansi-styles": { - "version": "4.3.0", + "node_modules/playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", "dev": true, - "requires": { - "color-convert": "^2.0.1" + "dependencies": { + "playwright-core": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "anymatch": { - "version": "3.1.3", + "node_modules/playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" } }, - "aproba": { - "version": "2.0.0", - "dev": true - }, - "are-we-there-yet": { - "version": "2.0.0", + "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, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "argparse": { - "version": "1.0.10", + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">= 0.4" } }, - "assertion-error": { - "version": "1.1.0", - "dev": true - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", - "dev": true - }, - "balanced-match": { + "node_modules/potpack": { "version": "1.0.2", - "dev": true + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" }, - "base64-js": { - "version": "1.5.1", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "dev": true - }, - "bl": { - "version": "4.1.0", + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "boolbase": { - "version": "1.0.0", - "dev": true + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" }, - "brace-expansion": { - "version": "1.1.11", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" } }, - "braces": { - "version": "3.0.2", + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "browser-stdout": { - "version": "1.3.1", - "dev": true - }, - "buffer": { - "version": "5.7.1", + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "call-bind": { - "version": "1.0.2", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "camel-case": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", - "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, - "requires": { - "sentence-case": "^1.1.1", - "upper-case": "^1.1.1" + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" } }, - "camelcase": { - "version": "6.3.0", - "dev": true - }, - "canvas": { - "version": "2.11.0", + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, - "requires": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, "dependencies": { - "decompress-response": { - "version": "4.2.1", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "mimic-response": { - "version": "2.1.0", - "dev": true - }, - "simple-get": { - "version": "3.1.1", - "dev": true, - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "chai": { - "version": "4.3.7", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" } }, - "chalk": { - "version": "4.1.2", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "dependencies": { - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "check-error": { - "version": "1.0.2", - "dev": true - }, - "chokidar": { - "version": "3.5.3", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "chownr": { - "version": "1.1.4", - "dev": true - }, - "chroma-js": { - "version": "1.4.1" + "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==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } }, - "cliui": { - "version": "7.0.4", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "color": { - "version": "4.2.3", + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "color-convert": { - "version": "2.0.1", + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { - "color-name": "~1.1.4" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { - "color-name": { - "version": "1.1.4", - "dev": true - } + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", "dev": true }, - "color-namer": { - "version": "1.4.0", - "requires": { - "chroma-js": "^1.3.4", - "es6-weak-map": "^2.0.3" - } - }, - "color-parse": { - "version": "1.4.2", - "requires": { - "color-name": "^1.0.0" - }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, "dependencies": { - "color-name": { - "version": "1.1.4" - } - } - }, - "color-rgba": { - "version": "2.4.0", - "requires": { - "color-parse": "^1.4.2", - "color-space": "^2.0.0" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "color-space": { - "version": "2.0.0" + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "color-string": { - "version": "1.9.1", + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - }, "dependencies": { - "color-name": { - "version": "1.1.4", - "dev": true - } + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "color-support": { - "version": "1.1.3", - "dev": true - }, - "colors": { - "version": "1.2.5", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "commander": { - "version": "10.0.0", - "dev": true + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "concat-map": { - "version": "0.0.1", - "dev": true + "node_modules/sentence-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", + "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } }, - "console-control-strings": { - "version": "1.1.0", + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "copy-paste-win32fix": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/copy-paste-win32fix/-/copy-paste-win32fix-1.4.0.tgz", - "integrity": "sha512-covImiUCOAvAPzWHWwrcEJYOPZVdAvxp5PBbSdtlK1xt2O5IJsBOw+1cQTLhP2L4ph4syKfbDFzKRlNGyjbBSw==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "requires": { - "iconv-lite": "^0.4.8" + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "cross-spawn": { - "version": "6.0.5", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, "dependencies": { - "semver": { - "version": "5.7.1", - "dev": true - } + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "css-select": { - "version": "4.2.1", + "node_modules/sharp": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", + "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.0" + }, + "engines": { + "libvips": ">=8.15.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.4", + "@img/sharp-darwin-x64": "0.33.4", + "@img/sharp-libvips-darwin-arm64": "1.0.2", + "@img/sharp-libvips-darwin-x64": "1.0.2", + "@img/sharp-libvips-linux-arm": "1.0.2", + "@img/sharp-libvips-linux-arm64": "1.0.2", + "@img/sharp-libvips-linux-s390x": "1.0.2", + "@img/sharp-libvips-linux-x64": "1.0.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", + "@img/sharp-libvips-linuxmusl-x64": "1.0.2", + "@img/sharp-linux-arm": "0.33.4", + "@img/sharp-linux-arm64": "0.33.4", + "@img/sharp-linux-s390x": "0.33.4", + "@img/sharp-linux-x64": "0.33.4", + "@img/sharp-linuxmusl-arm64": "0.33.4", + "@img/sharp-linuxmusl-x64": "0.33.4", + "@img/sharp-wasm32": "0.33.4", + "@img/sharp-win32-ia32": "0.33.4", + "@img/sharp-win32-x64": "0.33.4" } }, - "css-tree": { - "version": "1.1.3", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "css-what": { - "version": "5.1.0", - "dev": true - }, - "csscolorparser": { - "version": "1.0.3" - }, - "csso": { - "version": "4.2.0", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "requires": { - "css-tree": "^1.1.2" + "engines": { + "node": ">=8" } }, - "d": { - "version": "1.0.1", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "debug": { - "version": "4.3.4", + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, - "requires": { - "ms": "2.1.2" - }, "dependencies": { - "ms": { - "version": "2.1.2", - "dev": true - } + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" } }, - "decamelize": { - "version": "4.0.0", - "dev": true - }, - "decompress-response": { - "version": "6.0.0", + "node_modules/shelljs/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { - "mimic-response": "^3.1.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "deep-eql": { - "version": "4.1.2", + "node_modules/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { - "type-detect": "^4.0.0" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "deep-extend": { - "version": "0.6.0", - "dev": true - }, - "define-lazy-prop": { - "version": "2.0.0", - "dev": true - }, - "define-properties": { - "version": "1.1.3", + "node_modules/shelljs/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "object-keys": "^1.0.12" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "delegates": { - "version": "1.0.0", - "dev": true - }, - "detect-libc": { - "version": "2.0.1", - "dev": true - }, - "diff": { - "version": "5.0.0", - "dev": true - }, - "dom-serializer": { - "version": "1.3.2", + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, "dependencies": { - "entities": { - "version": "2.2.0", - "dev": true - } + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, - "domelementtype": { - "version": "2.2.0", - "dev": true + "node_modules/shortid": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", + "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "optional": true, + "dependencies": { + "nanoid": "^2.1.0" + } }, - "domhandler": { - "version": "4.3.0", + "node_modules/shx": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", + "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", "dev": true, - "requires": { - "domelementtype": "^2.2.0" + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "domutils": { - "version": "2.8.0", + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "earcut": { - "version": "2.2.4" + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "emoji-regex": { - "version": "8.0.0", - "dev": true + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "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" + } + ] }, - "end-of-stream": { - "version": "1.4.4", + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", "dev": true, - "requires": { - "once": "^1.4.0" + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" } }, - "error-ex": { - "version": "1.3.2", + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" + "dependencies": { + "is-arrayish": "^0.3.1" } }, - "es-abstract": { - "version": "1.19.1", + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/sort-asc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", + "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "engines": { + "node": ">=0.10.0" } }, - "es-to-primitive": { - "version": "1.2.1", + "node_modules/sort-desc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", + "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==", "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "engines": { + "node": ">=0.10.0" } }, - "es5-ext": { - "version": "0.10.62", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "node_modules/sort-object": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", + "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", + "dev": true, + "dependencies": { + "sort-asc": "^0.1.0", + "sort-desc": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "es6-symbol": { - "version": "3.1.3", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "es6-weak-map": { - "version": "2.0.3", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "esbuild": { - "version": "0.17.11", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.17.11", - "@esbuild/android-arm64": "0.17.11", - "@esbuild/android-x64": "0.17.11", - "@esbuild/darwin-arm64": "0.17.11", - "@esbuild/darwin-x64": "0.17.11", - "@esbuild/freebsd-arm64": "0.17.11", - "@esbuild/freebsd-x64": "0.17.11", - "@esbuild/linux-arm": "0.17.11", - "@esbuild/linux-arm64": "0.17.11", - "@esbuild/linux-ia32": "0.17.11", - "@esbuild/linux-loong64": "0.17.11", - "@esbuild/linux-mips64el": "0.17.11", - "@esbuild/linux-ppc64": "0.17.11", - "@esbuild/linux-riscv64": "0.17.11", - "@esbuild/linux-s390x": "0.17.11", - "@esbuild/linux-x64": "0.17.11", - "@esbuild/netbsd-x64": "0.17.11", - "@esbuild/openbsd-x64": "0.17.11", - "@esbuild/sunos-x64": "0.17.11", - "@esbuild/win32-arm64": "0.17.11", - "@esbuild/win32-ia32": "0.17.11", - "@esbuild/win32-x64": "0.17.11" - } - }, - "escalade": { - "version": "3.1.1", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "dev": true - }, - "events": { - "version": "3.3.0" - }, - "expand-template": { - "version": "2.0.3", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, - "ext": { - "version": "1.7.0", - "requires": { - "type": "^2.7.2" - }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "dependencies": { - "type": { - "version": "2.7.2" - } + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "node_modules/spdx-license-ids": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "dev": true + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", "dev": true }, - "fill-range": { - "version": "7.0.1", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "find-up": { - "version": "5.0.0", + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flat": { - "version": "5.0.2", - "dev": true - }, - "fonteditor-core": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/fonteditor-core/-/fonteditor-core-2.1.11.tgz", - "integrity": "sha512-zgO5tHw5vzqYqkeb9jthIANbOOGwWqNWovINCMyIDLh9xmujsHPqloWJdk0PMbJHbZmiLG1F/kygfuWS8+TDfA==", - "requires": { - "@xmldom/xmldom": "^0.8.3" + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "fs-constants": { - "version": "1.0.0", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "fs-minipass": { - "version": "2.1.0", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "requires": { - "minipass": "^3.0.0" + "engines": { + "node": ">=12" }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - } + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "fs.realpath": { - "version": "1.0.0", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "dev": true - }, - "gauge": { - "version": "3.0.2", + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "geojson-validation": { - "version": "1.0.2" - }, - "geojson-vt": { - "version": "3.2.1" - }, - "get-caller-file": { - "version": "2.0.5", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "get-stream": { - "version": "6.0.1" - }, - "get-symbol-description": { - "version": "1.0.0", + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "github-from-package": { - "version": "0.0.0", - "dev": true - }, - "gl-matrix": { - "version": "3.4.3" - }, - "glob": { - "version": "9.2.1", + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^7.4.1", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "7.4.2", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "glob-parent": { - "version": "5.1.2", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "global-prefix": { - "version": "3.0.0", - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "google-font-installer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/google-font-installer/-/google-font-installer-1.2.0.tgz", - "integrity": "sha512-0tgwTRphnjiTEUflczlgJU0PVao1Y/oA0ZPYH3d4Twc7Uc8SL36mQyjzxY8KXaVUjID++XDFrGo3aJVNDodE3Q==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "async": "^1.5.2", - "colors": "^1.1.2", - "commander": "^2.9.0", - "copy-paste-win32fix": "^1.4.0", - "file-type": "^3.6.0", - "mv": "^2.1.1", - "node-powershell": "^4.0.0", - "pascal-case": "^1.1.2" - }, "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "graceful-fs": { - "version": "4.2.10", - "dev": true - }, - "has": { - "version": "1.0.3", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "engines": { + "node": ">=4" } }, - "has-bigints": { - "version": "1.0.1", - "dev": true + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "has-flag": { - "version": "4.0.0", - "dev": true + "node_modules/supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "dependencies": { + "kdbush": "^3.0.0" + } }, - "has-symbols": { - "version": "1.0.2", - "dev": true + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "has-tostringtag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "requires": { - "has-symbols": "^1.0.2" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has-unicode": { - "version": "2.0.1", - "dev": true - }, - "he": { - "version": "1.2.0", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "dev": true - }, - "https-proxy-agent": { - "version": "5.0.1", + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1" - }, - "inflight": { - "version": "1.0.6", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "dev": true - }, - "ini": { - "version": "1.3.8" - }, - "internal-slot": { - "version": "1.0.3", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "interpret": { - "version": "1.4.0", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.4", - "dev": true - }, - "is-core-module": { - "version": "2.9.0", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-negative-zero": { - "version": "2.0.2", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "dev": true - }, - "is-number-object": { - "version": "1.0.6", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-plain-obj": { - "version": "2.1.0", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-wsl": { - "version": "2.2.0", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isexe": { - "version": "2.0.0" - }, - "js-yaml": { - "version": "4.1.0", - "dev": true, - "requires": { - "argparse": "^2.0.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "dev": true - } - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "dev": true - }, - "json-stringify-pretty-compact": { - "version": "2.0.0", - "dev": true - }, - "kdbush": { - "version": "3.0.0" - }, - "kind-of": { - "version": "6.0.3" - }, - "load-json-file": { - "version": "4.0.0", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "locate-path": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "loupe": { - "version": "2.3.6", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "maplibre-gl": { - "version": "2.4.0", - "requires": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^2.0.1", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.5", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "@types/geojson": "^7946.0.10", - "@types/mapbox__point-geometry": "^0.1.2", - "@types/mapbox__vector-tile": "^1.3.0", - "@types/pbf": "^3.0.2", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.4", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.4.3", - "global-prefix": "^3.0.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.2", - "quickselect": "^2.0.0", - "supercluster": "^7.1.5", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.3" - } - }, - "mdn-data": { - "version": "2.0.14", - "dev": true - }, - "memorystream": { - "version": "0.3.1", - "dev": true - }, - "mimic-response": { - "version": "3.1.0", - "dev": true - }, - "minimatch": { - "version": "3.1.1", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6" - }, - "minipass": { - "version": "4.2.4", - "dev": true - }, - "minizlib": { - "version": "2.1.2", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "mkdirp": { - "version": "1.0.4", - "dev": true - }, - "mkdirp-classic": { - "version": "0.5.3", - "dev": true - }, - "mocha": { - "version": "10.1.0", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "glob": { - "version": "7.2.0", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "minimatch": { - "version": "5.0.1", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } - } - }, - "strip-json-comments": { - "version": "3.1.1", - "dev": true - } - } - }, - "ms": { - "version": "2.1.3", - "dev": true - }, - "murmurhash-js": { - "version": "1.0.0" - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "dev": true, - "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", - "dev": true, - "requires": { - "glob": "^6.0.1" - } - } - } - }, - "nan": { - "version": "2.17.0", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "dev": true - }, - "napi-build-utils": { - "version": "1.0.2", - "dev": true - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "dev": true - }, - "next-tick": { - "version": "1.1.0" - }, - "nice-try": { - "version": "1.0.5", - "dev": true - }, - "node-abi": { - "version": "3.33.0", - "dev": true, - "requires": { - "semver": "^7.3.5" - } - }, - "node-addon-api": { - "version": "5.1.0", - "dev": true - }, - "node-fetch": { - "version": "2.6.9", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-powershell": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/node-powershell/-/node-powershell-4.0.0.tgz", - "integrity": "sha512-WCZMLgwkjW9G/DZsZwyCEAXhMMzShLRUlnYS+EETRqRLSdUMbuO4xiQxIOeAutwQgvj75NvC58CorHFlx0olIA==", - "dev": true, - "optional": true, - "requires": { - "chalk": "^2.4.1", - "shortid": "^2.2.14" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "optional": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "optional": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "optional": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "optional": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "nopt": { - "version": "5.0.0", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" }, - "dependencies": { - "semver": { - "version": "5.7.1", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "dev": true - }, - "npm-run-all": { - "version": "4.1.5", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" + "bin": { + "svgo": "bin/svgo" }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "npmlog": { - "version": "5.0.1", - "dev": true, - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "nth-check": { - "version": "2.0.1", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "dev": true - }, - "object-inspect": { - "version": "1.12.0", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "once": { - "version": "1.4.0", - "dev": true, - "requires": { - "wrappy": "1" + "engines": { + "node": ">=10.13.0" } }, - "open": { - "version": "8.4.2", + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "openmapsamples": { - "version": "git+ssh://git@github.com/adamfranco/OpenMapSamples.git#5014e9f74d7fd49b4e57bc2e8045d3e0b913f221", - "from": "openmapsamples@github:adamfranco/OpenMapSamples", - "requires": { - "geojson-validation": "^1.0.2" - } - }, - "openmapsamples-maplibre": { - "version": "git+ssh://git@github.com/adamfranco/OpenMapSamples-MapLibre.git#d0bb7b897c95bd0ef6adcd4e3a2ad409b9266c27", - "from": "openmapsamples-maplibre@github:adamfranco/OpenMapSamples-MapLibre", - "requires": { - "maplibre-gl": "^2.0", - "openmapsamples": "github:adamfranco/OpenMapSamples" + "engines": { + "node": ">= 10" } }, - "p-limit": { - "version": "3.1.0", + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, - "requires": { - "yocto-queue": "^0.1.0" + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "p-locate": { + "node_modules/tar/node_modules/minipass": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "pascal-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", - "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", - "dev": true, - "requires": { - "camel-case": "^1.1.1", - "upper-case-first": "^1.1.0" + "engines": { + "node": ">=8" } }, - "path-exists": { - "version": "4.0.0", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "dev": true - }, - "path-scurry": { - "version": "1.6.1", + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "requires": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" + "bin": { + "mkdirp": "bin/cmd.js" }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "dev": true - } + "engines": { + "node": ">=10" } }, - "pathval": { - "version": "1.1.1", - "dev": true + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" }, - "pbf": { - "version": "3.2.1", - "requires": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" + "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==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "picocolors": { - "version": "1.0.0", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "dev": true - }, - "pidtree": { - "version": "0.3.1", - "dev": true - }, - "pify": { - "version": "3.0.0", - "dev": true - }, - "potpack": { - "version": "1.0.2" - }, - "prebuild-install": { - "version": "7.1.1", - "dev": true, - "requires": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } + "node_modules/tokenfield": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/tokenfield/-/tokenfield-1.5.2.tgz", + "integrity": "sha512-G1mSawKJLSf3RqcI00uojA5BD2WAo+gc3ZyU4ElHFY08RPaksnJPcyEV1PAGPgAVZmQTjFGQ2uLK6WVDJrgTLA==" }, - "prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, - "protocol-buffers-schema": { - "version": "3.6.0" - }, - "pump": { - "version": "3.0.0", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "quickselect": { - "version": "2.0.0" - }, - "randombytes": { - "version": "2.1.0", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "rc": { - "version": "1.2.8", + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } + "optional": true }, - "read-pkg": { - "version": "3.0.0", + "node_modules/tsx": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.11.2.tgz", + "integrity": "sha512-V5DL5v1BuItjsQ2FN9+4OjR7n5cr8hSgN+VGmm/fd2/0cgQdBIWHcQ3bFYm/5ZTmyxkTDBUIaRuW2divgfPe0A==", "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, "dependencies": { - "path-type": { - "version": "3.0.0", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - } - } - }, - "readable-stream": { - "version": "3.6.1", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.6.2", - "dev": true, - "requires": { - "resolve": "^1.1.6" + "esbuild": "~0.20.2", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "require-directory": { - "version": "2.1.1", - "dev": true - }, - "resolve": { - "version": "1.22.0", + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-protobuf-schema": { - "version": "2.1.0", - "requires": { - "protocol-buffers-schema": "^3.3.1" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "rimraf": { - "version": "3.0.2", + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "rw": { - "version": "1.3.3", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "7.3.7", + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "lru-cache": "^6.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "sentence-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", - "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "lower-case": "^1.1.1" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "serialize-javascript": { - "version": "6.0.0", + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "randombytes": "^2.1.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "set-blocking": { - "version": "2.0.0", - "dev": true - }, - "sharp": { - "version": "0.30.7", + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "shebang-command": { - "version": "1.2.0", + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "shebang-regex": "^1.0.0" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "shebang-regex": { - "version": "1.0.0", - "dev": true - }, - "shell-quote": { - "version": "1.7.3", - "dev": true - }, - "shelljs": { - "version": "0.8.5", + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "shortid": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", - "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "requires": { - "nanoid": "^2.1.0" - }, - "dependencies": { - "nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", - "dev": true, - "optional": true - } + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "shx": { - "version": "0.3.4", + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], "dev": true, - "requires": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "side-channel": { - "version": "1.0.4", + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "signal-exit": { - "version": "3.0.7", - "dev": true - }, - "simple-concat": { - "version": "1.0.1", - "dev": true - }, - "simple-get": { - "version": "4.0.1", + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], "dev": true, - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "simple-swizzle": { - "version": "0.2.2", + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "dev": true - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "sort-asc": { - "version": "0.1.0", - "dev": true - }, - "sort-desc": { - "version": "0.1.1", - "dev": true - }, - "sort-object": { - "version": "0.3.2", + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], "dev": true, - "requires": { - "sort-asc": "^0.1.0", - "sort-desc": "^0.1.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "source-map": { - "version": "0.6.1", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "spdx-exceptions": { - "version": "2.3.0", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "spdx-license-ids": { - "version": "3.0.11", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "dev": true - }, - "stable": { - "version": "0.1.8", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "dev": true - } + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" } }, - "string-argv": { - "version": "0.3.1", - "dev": true - }, - "string-width": { - "version": "4.2.3", + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" } }, - "string.prototype.padend": { - "version": "3.1.3", + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" } }, - "string.prototype.trimend": { - "version": "1.0.4", + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "string.prototype.trimstart": { - "version": "1.0.4", + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "strip-ansi": { - "version": "6.0.1", + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "ansi-regex": "^5.0.1" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "strip-bom": { - "version": "3.0.0", - "dev": true + "node_modules/tsx/node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } }, - "strip-json-comments": { - "version": "2.0.1", + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", "dev": true }, - "supercluster": { - "version": "7.1.5", - "requires": { - "kdbush": "^3.0.0" - } - }, - "supports-color": { - "version": "8.1.1", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "requires": { - "has-flag": "^4.0.0" + "engines": { + "node": ">=4" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true - }, - "svgo": { - "version": "2.8.0", + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, "dependencies": { - "commander": { - "version": "7.2.0", - "dev": true - } + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" } }, - "tar": { - "version": "6.1.13", + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, "dependencies": { - "chownr": { - "version": "2.0.0", - "dev": true - } + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "tar-fs": { - "version": "2.1.1", + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "tar-stream": { - "version": "2.2.0", + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "tinyqueue": { - "version": "2.0.3" - }, - "to-regex-range": { - "version": "5.0.1", + "node_modules/typedoc": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", + "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", "dev": true, - "requires": { - "is-number": "^7.0.0" + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.0", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" } }, - "tokenfield": { - "version": "1.5.2" - }, - "tr46": { - "version": "0.0.3", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "requires": { - "safe-buffer": "^5.0.1" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" } }, - "type": { - "version": "1.2.0" - }, - "type-detect": { - "version": "4.0.8", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.1", + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "upper-case": { + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", "dev": true }, - "upper-case-first": { + "node_modules/upper-case-first": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", "dev": true, - "requires": { + "dependencies": { "upper-case": "^1.1.1" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "validate-npm-package-license": { + "node_modules/validate-npm-package-license": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "requires": { + "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, - "vt-pbf": { + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/vt-pbf": { "version": "3.1.3", - "requires": { + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": { "@mapbox/point-geometry": "0.1.0", "@mapbox/vector-tile": "^1.3.1", "pbf": "^3.2.1" } }, - "webidl-conversions": { + "node_modules/webidl-conversions": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, - "whatwg-url": { + "node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, - "requires": { + "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, - "which": { - "version": "1.3.1", - "requires": { + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "which-boxed-primitive": { + "node_modules/which-boxed-primitive": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, - "requires": { + "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", "is-number-object": "^1.0.4", "is-string": "^1.0.5", "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "wide-align": { + "node_modules/wide-align": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, - "requires": { + "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "workerpool": { - "version": "6.2.1", + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "wrap-ansi": { + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { - "version": "1.0.2", + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "y18n": { + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/y18n": { "version": "5.0.8", - "dev": true + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yargs": { + "node_modules/yargs": { "version": "16.2.0", - "dev": true, - "requires": { + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -6157,25 +6242,82 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "20.2.4", - "dev": true + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "engines": { + "node": ">=10" + } }, - "yargs-unparser": { + "node_modules/yargs-unparser": { "version": "2.0.0", - "dev": true, - "requires": { + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", - "dev": true + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "shieldlib": { + "version": "0.0.5", + "license": "CC0-1.0", + "dependencies": { + "@types/node": "^20.14.0", + "color-rgba": "^2.4.0", + "maplibre-gl": "^2.4.0", + "mocha": "^10.4.0" + }, + "devDependencies": { + "@types/color-rgba": "^2.1.2", + "canvas": "^2.11.2", + "esbuild": "^0.17.19", + "npm-run-all": "^4.1.5", + "prettier": "^2.8.8", + "shx": "^0.3.4", + "tsx": "^4.11.2", + "typedoc": "^0.24.8", + "typescript": "^4.9.5" + } } } } diff --git a/package.json b/package.json index b9391a6d1..a55d536da 100644 --- a/package.json +++ b/package.json @@ -7,32 +7,38 @@ "type": "git", "url": "git+https://github.com/zelonewolf/openstreetmap-americana.git" }, + "workspaces": [ + "shieldlib" + ], "scripts": { + "build:shieldlib": "cd shieldlib && run-s build:code docs", + "build:code": "tsx scripts/build", + "build": "run-s clean-build sprites build:shieldlib build:code taginfo status_map", + "clean": "run-s clean:shieldlib clean:code clean-download clean-build", + "clean-download": "shx rm -rf download", + "clean-build": "shx rm -rf dist build", + "clean:shieldlib": "cd shieldlib && shx rm -rf dist docs", + "clean:code": "shx rm -rf dist", "config": "shx cp src/configs/config.maptiler.js src/config.js", "code_format": "run-s code_format:prettier code_format:svgo", "code_format:prettier": "prettier --write --list-different .", "code_format:svgo": "svgo -q -f icons/", - "clean": "run-s clean-download clean-build", - "clean-download": "shx rm -rf download", - "clean-build": "shx rm -rf dist build", - "fonts:install_glyph_builder": "cargo install build_pbf_glyphs@1.3.0", - "fonts:build": "node scripts/fonts", - "fonts": "run-s fonts:install_glyph_builder fonts:build", + "extract_layer": "node scripts/extract_layer", + "generate_samples": "tsx scripts/generate_samples.ts", + "icon_grid": "tsx scripts/icon_grid.ts", "presprites": "shx rm -rf dist/sprites", - "sprites": "node scripts/sprites.js", - "status_map": "node scripts/status_map.js", - "taginfo": "node scripts/taginfo.js", - "start": "run-s clean-build sprites fonts serve", - "serve": "node scripts/serve.js", - "build:code": "node scripts/build.js", - "build": "run-s clean-build sprites fonts build:code taginfo status_map", - "test": "mocha", + "serve": "tsx scripts/serve", "shields": "node scripts/generate_shield_defs.js -o dist/shields.json", + "sprites": "node scripts/sprites.js", + "start": "run-s clean-build build:shieldlib sprites shields serve", "stats": "node scripts/stats.js", - "style": "node scripts/generate_style.js -o dist/style.json" + "style": "node scripts/generate_style.js -o dist/style.json", + "status_map": "node scripts/status_map.js", + "taginfo": "tsx scripts/taginfo", + "test": "mocha" }, "dependencies": { - "color-namer": "^1.4.0", + "@americana/maplibre-shield-generator": "*", "color-rgba": "^2.4.0", "events": "^3.3.0", "fonteditor-core": "^2.1.11", @@ -42,23 +48,36 @@ "tokenfield": "^1.5.2" }, "devDependencies": { - "@basemaps/sprites": "^6.32.1", + "@basemaps/sprites": "^7.0.0", + "@mapbox/vector-tile": "^1.3.1", "@maplibre/maplibre-gl-style-spec": "^17.0.1", + "@playwright/test": "^1.38.1", + "@types/chai": "^4.3.4", + "@types/color-namer": "^1.3.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.8.4", + "benchmark": "^2.1.4", "canvas": "^2.11.0", "chai": "^4.3.7", + "color-namer": "^1.4.0", "commander": "^10.0.0", "esbuild": "^0.17.4", - "glob": "^9.2.1", + "glob": "^10.3.10", "google-font-installer": "^1.2.0", + "maplibre-gl": "^2.4.0", "mocha": "^10.1.0", "npm-run-all": "^4.1.5", "open": "^8.4.2", - "prettier": "2.3.2", + "pbf": "^3.2.1", + "prettier": "^2.3.2", + "sharp": "^0.33.2", "shx": "^0.3.4", - "svgo": "^2.8.0" + "svgo": "^2.8.0", + "tsx": "^4.6.2", + "typescript": "^4.9.5" }, "engines": { "npm": ">=8.3.0", - "node": ">=14" + "node": ">=18" } } diff --git a/scripts/FONTS.md b/scripts/FONTS.md deleted file mode 100644 index 7d577a345..000000000 --- a/scripts/FONTS.md +++ /dev/null @@ -1,8 +0,0 @@ -The Americana fontstack is built from [Google Fonts](https://fonts.google.com/). - -In order to add fontstack support, modify the `fonts.json` file as follows: - -1. Add font-family and variant information to the `font-families` section. The font-family is the name of the font as listed on Google Fonts, e.g. "Noto Sans". Use `gfi download ""` to get a full list of the variants. The variant is everything to the right of the dash in the filename, so if a file is named `NotoSans-700.ttf`, the variant is `700`, though it will be listed in Google as something like "Bold 700". The `gfi` command requires you to install the `google-font-installer` package into npm with `npm install -g google-font-installer`. -2. Define the range of characters that you want rendered in this font in the `glyph-ranges` section. Since this is JSON, you'll have to convert hex to decimal here. The named parameter is simply a name that is used in the rest of the file to refer to this range of characters. This is an _inclusive_ range so for example `[0, 255]` will include codepoint 0 and codepoint 255. -3. The `custom-font-stacks` section lists each font-stack and which font/glyph range combinations should be included in that fontstack. -4. The `bundle-font-stacks` section lists all font stacks which should be bundled in their original form. diff --git a/scripts/benchmark.js b/scripts/benchmark.js new file mode 100644 index 000000000..abe4a0e96 --- /dev/null +++ b/scripts/benchmark.js @@ -0,0 +1,59 @@ +import Benchmark from "benchmark"; +import { expression } from "@maplibre/maplibre-gl-style-spec"; + +import { build } from "../src/layer/index.js"; +import { VectorTile } from "@mapbox/vector-tile"; +import Pbf from "pbf"; + +const layers = build(["en"]) + .filter((layer) => layer["source-layer"] === "transportation" && layer.filter) + .map((layer) => expression.createExpression(layer.filter).value.expression); + +const suite = new Benchmark.Suite(); + +async function addTest(name, z, x, y) { + const tile = await ( + await fetch( + `https://d1zqyi8v6vm8p9.cloudfront.net/planet/${z}/${x}/${y}.mvt` + ) + ).arrayBuffer(); + + const transportation = new VectorTile(new Pbf(tile)).layers["transportation"]; + const features = []; + + for (let i = 0; i < transportation.length; i++) { + const feature = transportation.feature(i); + features.push({ + type: feature.type, + properties: feature.properties, + geometry: [], + }); + } + suite.add(`evaluate expressions ${name}`, () => { + let num = 0; + const context = { + properties: () => context.feature.properties, + geometryType: () => context.feature.type, + }; + for (const layer of layers) { + for (const feature of features) { + context.feature = feature; + if (layer.evaluate(context)) { + num++; + } + } + } + }); +} + +await addTest("nyc z12", 12, 1207, 1539); +await addTest("boston z12", 12, 1239, 1514); +await addTest("kansas z14", 14, 3707, 6302); + +suite + .on("error", (event) => console.log(event.target.error)) + .on("cycle", (event) => { + const time = 1_000 / event.target.hz; + console.log(`${time.toPrecision(4)}ms ${event.target}`); + }) + .run(); diff --git a/scripts/build.js b/scripts/build.js deleted file mode 100644 index 5b56e9204..000000000 --- a/scripts/build.js +++ /dev/null @@ -1,68 +0,0 @@ -import { stat, copyFile, mkdir } from "node:fs/promises"; -import { pathToFileURL } from "node:url"; - -import esbuild from "esbuild"; - -const maybeLocalConfig = async (name = "local.config.js") => { - let exists = await stat(name) - .then((st) => st.isFile()) - .catch((err) => { - if (err.code !== "ENOENT") throw err; - }); - if (exists) { - console.log("Local config in use: %o", name); - return { - define: { - CONFIG_PATH: JSON.stringify("../" + name), - }, - }; - } -}; - -const buildWith = async (key, buildOptions) => { - await mkdir("dist", { recursive: true }); - await Promise.all( - ["index.html", "shieldtest.html", "favicon.ico"].map((f) => - copyFile(`src/${f}`, `dist/${f}`) - ) - ); - - const localConfig = await maybeLocalConfig(); - - const options = { - entryPoints: ["src/americana.js", "src/shieldtest.js"], - format: "esm", - bundle: true, - minify: true, - sourcemap: true, - outdir: "dist", - logLevel: "info", - ...localConfig, - ...buildOptions, - define: { - ...localConfig?.define, - ...buildOptions?.define, - }, - }; - return ( - esbuild[key](options) - // esbuild will pretty-print its own error messages; - // suppress node.js from printing the exception. - .catch(() => process.exit(1)) - ); -}; - -export const buildContext = (buildOptions = {}) => - buildWith("context", buildOptions); - -export const build = (buildOptions = {}) => buildWith("build", buildOptions); - -const mainModule = pathToFileURL(process.argv[1]).toString(); -const isMain = import.meta.url === mainModule; - -if (isMain) { - await build({ - // defaults to undefined, but force it to get optimized out - define: { "window.LIVE_RELOAD": "false" }, - }); -} diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 000000000..1ba418418 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,93 @@ +import { stat, copyFile, mkdir } from "node:fs/promises"; + +import esbuild, { BuildContext, BuildOptions } from "esbuild"; + +const maybeLocalConfig = async (name = "local.config.js") => { + let exists = await stat(name) + .then((st) => st.isFile()) + .catch((err) => { + if (err.code !== "ENOENT") throw err; + }); + if (exists) { + console.log("Local config in use: %o", name); + return { + define: { + CONFIG_PATH: JSON.stringify("../" + name), + }, + }; + } +}; + +const buildWith = async ( + key: string, + buildOptions: BuildOptions +): Promise => { + await mkdir("dist", { recursive: true }); + + await Promise.all( + [ + "index.html", + "bare_map.html", + "shieldtest.html", + "favicon.ico", + "fonts.css", + ].map((f) => copyFile(`src/${f}`, `dist/${f}`)) + ); + + const localConfig = await maybeLocalConfig(); + + const options = { + entryPoints: [ + "src/americana.js", + "src/bare_americana.js", + "src/shieldtest.js", + ], + format: "esm", + bundle: true, + minify: true, + sourcemap: true, + outdir: "dist", + logLevel: "info", + ...localConfig, + ...buildOptions, + define: { + ...localConfig?.define, + ...buildOptions?.define, + }, + }; + + const shieldLibOptions = { + entryPoints: ["shieldlib/src/index.ts"], + format: "esm", + bundle: true, + minify: true, + sourcemap: true, + outdir: "shieldlib/dist", + logLevel: "info", + ...localConfig, + ...buildOptions, + define: { + ...localConfig?.define, + ...buildOptions?.define, + }, + }; + + // esbuild will pretty-print its own error messages; + // suppress node.js from printing the exception. + const suppressErrors = () => process.exit(1); + + return await Promise.all([ + esbuild[key](options).catch(suppressErrors), + esbuild[key](shieldLibOptions).catch(suppressErrors), + ]); +}; + +export const buildContext = ( + buildOptions: BuildOptions = {} +): Promise => buildWith("context", buildOptions); + +export const build = ( + buildOptions: BuildOptions = {} +): Promise => buildWith("build", buildOptions); + +await build(); diff --git a/scripts/extract_layer.js b/scripts/extract_layer.js new file mode 100644 index 000000000..e6eb4cc3b --- /dev/null +++ b/scripts/extract_layer.js @@ -0,0 +1,56 @@ +import * as Style from "../src/js/style.js"; +import config from "../src/config.js"; +import { Command, Option } from "commander"; + +const program = new Command(); +program + .option("-pp, --pretty", "pretty-print JSON output") + .addOption( + new Option( + "-pg, --print-group ", + "print a list of the layers in a group" + ).conflicts("printLayer") + ) + .option("-pl, --print-layer ", "print the JSON of a layer") + .option("-loc, --locales ", "language codes", ["mul"]); +program.parse(process.argv); + +const opts = program.opts(); + +if (Object.keys(opts).length === 1) program.help(); + +const locales = opts.locales[0].split(","); + +const style = Style.build( + config.OPENMAPTILES_URL, + "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", + "https://osm-americana.github.io/fontstack66/{fontstack}/{range}.pbf", + locales +); + +const layers = style.layers; +const layerMap = new Map(); +const layerGroupMap = new Map(); + +for (let i = 0; i < layers.length; i++) { + const layer = layers[i]; + layerMap.set(layer.id, layers[i]); + const layerGroup = layer["source-layer"] || layer.source || layer.type; + if (!layerGroupMap.has(layerGroup)) { + layerGroupMap.set(layerGroup, [layer.id]); + } else { + layerGroupMap.get(layerGroup).push(layer.id); + } +} + +let outputObj; + +if (opts.printGroup) { + outputObj = layerGroupMap.get(opts.printGroup); +} + +if (opts.printLayer) { + outputObj = layerMap.get(opts.printLayer) ?? {}; +} + +process.stdout.write(JSON.stringify(outputObj, null, opts.pretty ? 2 : null)); diff --git a/scripts/folder_diff.ts b/scripts/folder_diff.ts new file mode 100644 index 000000000..93263faf8 --- /dev/null +++ b/scripts/folder_diff.ts @@ -0,0 +1,91 @@ +import fs from "fs"; +import { basename } from "path"; +import { execSync } from "child_process"; + +interface Viewport { + width: number; + height: number; +} + +interface LocationData { + location: string; + name: string; + viewport: Viewport; + controls?: boolean; +} + +type Locations = LocationData[]; + +import sampleLocationJSON from "../test/sample_locations.json"; + +const sampleLocations: Locations = sampleLocationJSON as Locations; + +function getLocationByName(name: string): string | undefined { + const locationData = sampleLocations.find( + (location) => location.name === name + ); + return locationData?.location; +} + +// Check if the right number of arguments are passed +if (process.argv.length !== 6) { + console.log("Usage: "); + process.exit(1); +} + +const [folder1, folder2, urlBase, sha] = process.argv.slice(2); +const outputFolder = "samples-diff"; + +// Create the output folder if it doesn't exist +if (!fs.existsSync(outputFolder)) { + fs.mkdirSync(outputFolder); +} + +// Loop through files in folder1 +fs.readdirSync(folder1) + .filter((file) => file.endsWith(".png")) + .forEach((file) => { + const basefile = basename(file); + + // Check if file exists in folder2 + if (fs.existsSync(`${folder2}/${basefile}`)) { + // Compare the files + try { + execSync(`cmp -s "${folder1}/${basefile}" "${folder2}/${basefile}"`); + } catch (e) { + // If files are different + fs.copyFileSync( + `${folder1}/${basefile}`, + `${outputFolder}/${basefile.split(".")[0]}_${sha}_before.png` + ); + fs.copyFileSync( + `${folder2}/${basefile}`, + `${outputFolder}/${basefile.split(".")[0]}_${sha}_after.png` + ); + } + } + }); + +const outputMD = "pr_preview-extra.md"; +let mdContent = + "## Map Changes\n| Sample | Current Render | This PR |\n|-------------|--------|-------|\n"; + +// Loop through *_before.png files in the output folder +fs.readdirSync(outputFolder) + .filter((file) => file.endsWith("_before.png")) + .forEach((before_file) => { + const basefile = basename(before_file, `_${sha}_before.png`); + + // Check if the after file exists + if (fs.existsSync(`${outputFolder}/${basefile}_${sha}_after.png`)) { + // Add an entry to the markdown table + const loc = getLocationByName(basefile); + mdContent += + `| ${basefile}
${loc}
[Current Render](https://zelonewolf.github.io/openstreetmap-americana/#map=${loc})` + + `
[This PR](${urlBase}#map=${loc}) ` + + `| ![Current Render](${urlBase}${outputFolder}/${basefile}_${sha}_before.png) |` + + ` ![This PR](${urlBase}${outputFolder}/${basefile}_${sha}_after.png) |\n`; + } + }); + +fs.writeFileSync(outputMD, mdContent); diff --git a/scripts/fonts.js b/scripts/fonts.js deleted file mode 100644 index 1b962d2d7..000000000 --- a/scripts/fonts.js +++ /dev/null @@ -1,168 +0,0 @@ -// read font file -import { Font } from "fonteditor-core"; -import fs from "fs"; -import { execSync } from "child_process"; -import { promisify } from "util"; -import { exec } from "child_process"; -import path from "path"; - -function createRangeArray(start, end) { - return Array.from({ length: end - start + 1 }, (_, i) => start + i); -} - -function countFilesMatchingRegex(folderPath, regexPattern) { - return fs.readdirSync(folderPath).filter((file) => regexPattern.test(file)) - .length; -} - -function fontRegex(fontName) { - return new RegExp( - `^${fontName.replace(/\s+/g, "")}-([1-9]00)?(regular|italic)?\\.[ot]tf$`, - "i" - ); -} - -function copyFolderContents(sourceFolder, destinationFolder) { - const files = fs.readdirSync(sourceFolder); - return Promise.all( - files.map((file) => - fs.promises.copyFile( - path.join(sourceFolder, file), - path.join(destinationFolder, file) - ) - ) - ); -} - -const downloadFolder = "download/font"; -const distFontFolder = "dist/fonts"; -const ttfFontFolder = "build/font/ttf"; -const bundleFontFolder = "download/font-bundle"; - -function loadGoogleFonts(fontSpec, destFolder) { - for (const fontFamily in fontSpec) { - if ( - fontSpec[fontFamily].length == - countFilesMatchingRegex(downloadFolder, fontRegex(fontFamily)) + - countFilesMatchingRegex(bundleFontFolder, fontRegex(fontFamily)) - ) { - console.log(`Already downloaded ${fontFamily}`); - continue; - } - - const variants = fontSpec[fontFamily].join(","); - - execSync( - //Requires google-font-installer - `npm exec -- gfi download "${fontFamily}" -v ${variants} -d ${destFolder}`, - (error, stdout, stderr) => { - if (error) { - console.log(`error: ${error.message}`); - return; - } - if (stderr) { - console.log(`stderr: ${stderr}`); - return; - } - console.log(`stdout: ${stdout}`); - } - ); - console.log(`Downloaded ${fontFamily}`); - } -} - -[downloadFolder, ttfFontFolder, distFontFolder, bundleFontFolder].forEach( - (folder) => fs.mkdirSync(folder, { recursive: true }) -); - -const fontDef = JSON.parse(fs.readFileSync("scripts/fonts.json")); -const fontRanges = fontDef["glyph-ranges"]; -const customFontStacks = fontDef["custom-font-stacks"]; -const bundleFontStacks = fontDef["bundle-font-stacks"]; -const fontFamilies = fontDef["font-families"]; - -loadGoogleFonts(fontFamilies, downloadFolder); - -//Just copy fonts that are used unmodified -loadGoogleFonts(bundleFontStacks, bundleFontFolder); - -copyFolderContents(bundleFontFolder, ttfFontFolder); - -for (const stack in customFontStacks) { - let font; - for (const stackPart in customFontStacks[stack]) { - let stackPartDef = customFontStacks[stack][stackPart]; - const inputFontBuffer = fs.readFileSync( - `${downloadFolder}/${stackPartDef.file}.ttf` - ); - - const ranges = stackPartDef.ranges; - for (const range in ranges) { - const namedRange = ranges[range]; - const numberedRanges = fontRanges[namedRange]; - for (const numberedRange in numberedRanges) { - const subsetRange = numberedRanges[numberedRange]; - const subsetGlyphArray = createRangeArray( - subsetRange[0], - subsetRange[1] - ); - const fontSegment = Font.create(inputFontBuffer, { - type: "ttf", - subset: subsetGlyphArray, - hinting: true, - compound2simple: true, - inflate: null, - combinePath: false, - }); - if (font === undefined) { - font = fontSegment; - font.data.name = { - copyright: "Repackaged from Google Fonts", - fontFamily: "Americana", - fullName: stack, - manufacturer: "OpenStreetMap Americana Team", - }; - } else { - font.merge(fontSegment); - } - } - } - } - - // write font file - const outputBuffer = font.write({ - // support ttf, woff, woff2, eot, svg - type: "ttf", - // save font hinting - hinting: true, - // deflate function for woff, eg. pako.deflate - deflate: null, - // for user to overwrite head.xMin, head.xMax, head.yMin, head.yMax, hhea etc. - support: { head: {}, hhea: {} }, - }); - - const ttfFile = `build/font/ttf/${stack}.ttf`; - fs.writeFileSync(ttfFile, outputBuffer); - console.log(`Built ${ttfFile}`); -} - -const pbfBuilderFilename = "~/.cargo/bin/build_pbf_glyphs"; - -async function buildPbf() { - try { - const { stdout, stderr } = await promisify(exec)( - `${pbfBuilderFilename} ${ttfFontFolder} ${distFontFolder}` - ); - console.log(`${stdout}`); - if (stderr) { - console.log(`PBF stderr: ${stderr}`); - } - } catch (error) { - console.log(`PBF build error: ${error.message}`); - process.exit(-1); - } -} - -buildPbf(); - -console.log("Generated PBF fontstack."); diff --git a/scripts/fonts.json b/scripts/fonts.json deleted file mode 100644 index a0e89ef2f..000000000 --- a/scripts/fonts.json +++ /dev/null @@ -1,610 +0,0 @@ -{ - "font-families": { - "Gothic A1": ["regular", "700"], - "M PLUS Rounded 1c": ["regular", "700"], - "Noto Sans": ["regular", "700", "italic", "700italic"], - "Noto Sans Arabic": ["regular", "700"], - "Noto Naskh Arabic": ["regular", "700"], - "Noto Sans Armenian": ["regular", "700"], - "Noto Sans Bengali": ["regular", "700"], - "Noto Sans Canadian Aboriginal": ["regular", "700"], - "Noto Sans Cherokee": ["regular", "700"], - "Noto Sans Devanagari": ["regular", "700"], - "Noto Sans Ethiopic": ["regular", "600"], - "Noto Sans Georgian": ["regular", "700"], - "Noto Sans Gujarati": ["regular", "700"], - "Noto Sans Gurmukhi": ["regular", "700"], - "Noto Sans Hebrew": ["regular", "700"], - "Noto Rashi Hebrew": ["regular", "700"], - "Noto Sans Kannada": ["regular", "700"], - "Noto Sans Khmer": ["regular", "700"], - "Noto Sans Lao": ["regular", "700"], - "Noto Sans Malayalam": ["regular", "700"], - "Noto Sans Myanmar": ["regular", "700"], - "Noto Sans Oriya": ["regular", "700"], - "Noto Sans Sinhala": ["regular", "700"], - "Noto Sans Symbols": ["regular", "700"], - "Noto Sans Tamil": ["regular", "700"], - "Noto Sans Thaana": ["regular", "700"], - "Noto Serif Tibetan": ["regular", "700"], - "Noto Sans Tifinagh": ["regular"], - "Noto Sans Telugu": ["regular", "700"], - "Noto Sans Thai": ["regular", "700"] - }, - "bundle-font-stacks": { - "Noto Sans HK": ["regular", "700"], - "Noto Sans JP": ["regular", "700"], - "Noto Sans KR": ["regular", "700"], - "Noto Sans SC": ["regular", "700"], - "Noto Sans TC": ["regular", "700"] - }, - "glyph-ranges": { - "arabic": [ - [1536, 1791], - [1872, 1919], - [2160, 2303], - [64336, 65023], - [65136, 65279], - [69216, 69247], - [69312, 69375], - [126064, 126143], - [126208, 126287], - [126464, 126719] - ], - "armenian": [ - [1328, 1423], - [64272, 64279] - ], - "bengali": [[2432, 2559]], - "bopomofo": [ - [12544, 12591], - [12704, 12735] - ], - "burmese": [[4096, 4255]], - "canadian_aboriginal": [ - [5120, 5487], - [6928, 6975], - [71216, 71231] - ], - "cherokee": [ - [5024, 5119], - [43888, 43967] - ], - "cyrillic": [ - [1024, 1327], - [7296, 7311], - [11744, 11775], - [42560, 42655], - [65070, 65071], - [122928, 123023] - ], - "devanagari": [[2304, 2431]], - "ethiopic": [ - [4608, 5017], - [11648, 11743], - [43776, 43823] - ], - "fullwidth": [[65280, 65519]], - "greek": [ - [880, 1023], - [7936, 8191], - [65856, 65999], - [119296, 119375] - ], - "georgian": [ - [4256, 4351], - [7312, 7359], - [11520, 11567] - ], - "gurmukhi": [[2560, 2687]], - "gujarati": [[2688, 2751]], - "han": [ - [11904, 12031], - [12032, 12245], - [12272, 12283], - [12284, 12287], - [12288, 12351] - ], - "hangul": [ - [4352, 4607], - [12592, 12687], - [43360, 43391], - [55216, 55295] - ], - "hebrew": [ - [1424, 1535], - [64280, 64335] - ], - "hiragana": [[12352, 12447]], - "kanbun": [[12688, 12703]], - "kannada": [[3200, 3327]], - "kanji": [[19968, 40959]], - "katakana": [ - [12448, 12543], - [12784, 12799] - ], - "khmer": [[6016, 6911]], - "latin": [ - [33, 879], - [7424, 7615], - [7680, 7935], - [8192, 8591], - [11360, 11391], - [42784, 43007], - [43824, 43887], - [64256, 64271], - [67456, 67519], - [122624, 122879] - ], - "lao": [[3712, 3839]], - "malayalam": [[3328, 3455]], - "oriya": [[2816, 2943]], - "sinhala": [[3456, 3583]], - "space": [[32, 32]], - "symbols": [[8592, 8703]], - "tamil": [[2944, 3071]], - "telugu": [[3072, 3199]], - "thaana": [[1920, 1983]], - "tibetan": [[3840, 4095]], - "tifinagh": [[11568, 11631]], - "thai": [[3584, 3711]] - }, - "custom-font-stacks": { - "Americana-Regular": [ - { - "file": "GothicA1-regular", - "ranges": ["space", "hangul"] - }, - { - "file": "MPlusRounded1C-regular", - "ranges": ["fullwidth", "hiragana", "kanbun", "katakana"] - }, - { - "file": "NotoSansArabic-regular", - "ranges": ["arabic"] - }, - { - "file": "NotoSansArmenian-regular", - "ranges": ["armenian"] - }, - { - "file": "NotoSansBengali-regular", - "ranges": ["bengali"] - }, - { - "file": "NotoSansCanadianAboriginal-regular", - "ranges": ["canadian_aboriginal"] - }, - { - "file": "NotoSansCherokee-regular", - "ranges": ["cherokee"] - }, - { - "file": "NotoSansDevanagari-regular", - "ranges": ["devanagari"] - }, - { - "file": "NotoSansEthiopic-regular", - "ranges": ["ethiopic"] - }, - { - "file": "NotoSansGeorgian-regular", - "ranges": ["georgian"] - }, - { - "file": "NotoSansGujarati-regular", - "ranges": ["gujarati"] - }, - { - "file": "NotoSansGurmukhi-regular", - "ranges": ["gurmukhi"] - }, - { - "file": "NotoSansHebrew-regular", - "ranges": ["hebrew"] - }, - { - "file": "NotoSansKannada-regular", - "ranges": ["kannada"] - }, - { - "file": "NotoSansKhmer-regular", - "ranges": ["khmer"] - }, - { - "file": "NotoSansLao-regular", - "ranges": ["lao"] - }, - { - "file": "NotoSansMalayalam-regular", - "ranges": ["malayalam"] - }, - { - "file": "NotoSansMyanmar-regular", - "ranges": ["burmese"] - }, - { - "file": "NotoSansOriya-regular", - "ranges": ["oriya"] - }, - { - "file": "NotoSansSinhala-regular", - "ranges": ["sinhala"] - }, - { - "file": "NotoSansSymbols-regular", - "ranges": ["symbols"] - }, - { - "file": "NotoSansTamil-regular", - "ranges": ["tamil"] - }, - { - "file": "NotoSansTelugu-regular", - "ranges": ["telugu"] - }, - { - "file": "NotoSansThaana-regular", - "ranges": ["thaana"] - }, - { - "file": "NotoSansThai-regular", - "ranges": ["thai"] - }, - { - "file": "NotoSerifTibetan-regular", - "ranges": ["tibetan"] - }, - { - "file": "NotoSansTifinagh-regular", - "ranges": ["tifinagh"] - }, - { - "file": "NotoSans-regular", - "ranges": ["cyrillic", "greek", "latin"] - } - ], - "Americana-Bold": [ - { - "file": "GothicA1-700", - "ranges": ["space", "hangul"] - }, - { - "file": "MPlusRounded1C-700", - "ranges": ["fullwidth", "hiragana", "kanbun", "katakana"] - }, - { - "file": "NotoSansArabic-700", - "ranges": ["arabic"] - }, - { - "file": "NotoSansArmenian-700", - "ranges": ["armenian"] - }, - { - "file": "NotoSansBengali-700", - "ranges": ["bengali"] - }, - { - "file": "NotoSansCanadianAboriginal-700", - "ranges": ["canadian_aboriginal"] - }, - { - "file": "NotoSansCherokee-700", - "ranges": ["cherokee"] - }, - { - "file": "NotoSansDevanagari-700", - "ranges": ["devanagari"] - }, - { - "file": "NotoSansEthiopic-600", - "ranges": ["ethiopic"] - }, - { - "file": "NotoSansGeorgian-700", - "ranges": ["georgian"] - }, - { - "file": "NotoSansGujarati-700", - "ranges": ["gujarati"] - }, - { - "file": "NotoSansGurmukhi-700", - "ranges": ["gurmukhi"] - }, - { - "file": "NotoSansKannada-700", - "ranges": ["kannada"] - }, - { - "file": "NotoSansHebrew-700", - "ranges": ["hebrew"] - }, - { - "file": "NotoSansKhmer-700", - "ranges": ["khmer"] - }, - { - "file": "NotoSansLao-700", - "ranges": ["lao"] - }, - { - "file": "NotoSansMalayalam-700", - "ranges": ["malayalam"] - }, - { - "file": "NotoSansMyanmar-700", - "ranges": ["burmese"] - }, - { - "file": "NotoSansOriya-700", - "ranges": ["oriya"] - }, - { - "file": "NotoSansSinhala-700", - "ranges": ["sinhala"] - }, - { - "file": "NotoSansSymbols-700", - "ranges": ["symbols"] - }, - { - "file": "NotoSansTamil-700", - "ranges": ["tamil"] - }, - { - "file": "NotoSansTelugu-700", - "ranges": ["telugu"] - }, - { - "file": "NotoSansThaana-700", - "ranges": ["thaana"] - }, - { - "file": "NotoSansThai-700", - "ranges": ["thai"] - }, - { - "file": "NotoSerifTibetan-700", - "ranges": ["tibetan"] - }, - { - "file": "NotoSansTifinagh-regular", - "ranges": ["tifinagh"] - }, - { - "file": "NotoSans-700", - "ranges": ["cyrillic", "greek", "latin"] - } - ], - "Americana-Italic": [ - { - "file": "GothicA1-regular", - "ranges": ["space", "hangul"] - }, - { - "file": "MPlusRounded1C-regular", - "ranges": ["fullwidth", "hiragana", "kanbun", "katakana"] - }, - { - "file": "NotoSansArabic-regular", - "ranges": ["arabic"] - }, - { - "file": "NotoSansArmenian-regular", - "ranges": ["armenian"] - }, - { - "file": "NotoSansBengali-regular", - "ranges": ["bengali"] - }, - { - "file": "NotoSansCanadianAboriginal-regular", - "ranges": ["canadian_aboriginal"] - }, - { - "file": "NotoSansCherokee-regular", - "ranges": ["cherokee"] - }, - { - "file": "NotoSansDevanagari-regular", - "ranges": ["devanagari"] - }, - { - "file": "NotoSansEthiopic-regular", - "ranges": ["ethiopic"] - }, - { - "file": "NotoSansGeorgian-regular", - "ranges": ["georgian"] - }, - { - "file": "NotoSansGujarati-regular", - "ranges": ["gujarati"] - }, - { - "file": "NotoSansGurmukhi-regular", - "ranges": ["gurmukhi"] - }, - { - "file": "NotoSansKannada-regular", - "ranges": ["kannada"] - }, - { - "file": "NotoSansKhmer-regular", - "ranges": ["khmer"] - }, - { - "file": "NotoSansLao-regular", - "ranges": ["lao"] - }, - { - "file": "NotoRashiHebrew-regular", - "ranges": ["hebrew"] - }, - { - "file": "NotoSansMalayalam-regular", - "ranges": ["malayalam"] - }, - { - "file": "NotoSansMyanmar-regular", - "ranges": ["burmese"] - }, - { - "file": "NotoSansOriya-regular", - "ranges": ["oriya"] - }, - { - "file": "NotoSansSinhala-regular", - "ranges": ["sinhala"] - }, - { - "file": "NotoSansSymbols-regular", - "ranges": ["symbols"] - }, - { - "file": "NotoSansTamil-regular", - "ranges": ["tamil"] - }, - { - "file": "NotoSansTelugu-regular", - "ranges": ["telugu"] - }, - { - "file": "NotoSansThaana-regular", - "ranges": ["thaana"] - }, - { - "file": "NotoSansThai-regular", - "ranges": ["thai"] - }, - { - "file": "NotoSerifTibetan-regular", - "ranges": ["tibetan"] - }, - { - "file": "NotoSansTifinagh-regular", - "ranges": ["tifinagh"] - }, - { - "file": "NotoSans-italic", - "ranges": ["cyrillic", "greek", "latin"] - } - ], - "Americana-Bold-Italic": [ - { - "file": "GothicA1-700", - "ranges": ["space", "hangul"] - }, - { - "file": "MPlusRounded1C-700", - "ranges": ["fullwidth", "hiragana", "kanbun", "katakana"] - }, - { - "file": "NotoSansArabic-700", - "ranges": ["arabic"] - }, - { - "file": "NotoSansArmenian-700", - "ranges": ["armenian"] - }, - { - "file": "NotoSansBengali-700", - "ranges": ["bengali"] - }, - { - "file": "NotoSansCanadianAboriginal-700", - "ranges": ["canadian_aboriginal"] - }, - { - "file": "NotoSansCherokee-700", - "ranges": ["cherokee"] - }, - { - "file": "NotoSansDevanagari-700", - "ranges": ["devanagari"] - }, - { - "file": "NotoSansEthiopic-600", - "ranges": ["ethiopic"] - }, - { - "file": "NotoSansGeorgian-700", - "ranges": ["georgian"] - }, - { - "file": "NotoSansGujarati-700", - "ranges": ["gujarati"] - }, - { - "file": "NotoSansGurmukhi-700", - "ranges": ["gurmukhi"] - }, - { - "file": "NotoSansKannada-700", - "ranges": ["kannada"] - }, - { - "file": "NotoSansKhmer-700", - "ranges": ["khmer"] - }, - { - "file": "NotoSansLao-700", - "ranges": ["lao"] - }, - { - "file": "NotoRashiHebrew-700", - "ranges": ["hebrew"] - }, - { - "file": "NotoSansMalayalam-700", - "ranges": ["malayalam"] - }, - { - "file": "NotoSansMyanmar-700", - "ranges": ["burmese"] - }, - { - "file": "NotoSansOriya-700", - "ranges": ["oriya"] - }, - { - "file": "NotoSansSinhala-700", - "ranges": ["sinhala"] - }, - { - "file": "NotoSansSymbols-700", - "ranges": ["symbols"] - }, - { - "file": "NotoSansTamil-700", - "ranges": ["tamil"] - }, - { - "file": "NotoSansTelugu-700", - "ranges": ["telugu"] - }, - { - "file": "NotoSansThaana-700", - "ranges": ["thaana"] - }, - { - "file": "NotoSansThai-700", - "ranges": ["thai"] - }, - { - "file": "NotoSerifTibetan-700", - "ranges": ["tibetan"] - }, - { - "file": "NotoSansTifinagh-regular", - "ranges": ["tifinagh"] - }, - { - "file": "NotoSans-700italic", - "ranges": ["cyrillic", "greek", "latin"] - } - ] - } -} diff --git a/scripts/generate_samples.ts b/scripts/generate_samples.ts new file mode 100644 index 000000000..2d0c6f5ce --- /dev/null +++ b/scripts/generate_samples.ts @@ -0,0 +1,109 @@ +import fs from "node:fs"; +import { + Browser, + BrowserContext, + chromium, + firefox, + webkit, +} from "@playwright/test"; +import type * as maplibre from "maplibre-gl"; + +// Declare a global augmentation for the Window interface +declare global { + interface WindowWithMap extends Window { + map?: maplibre.Map; + } +} + +type SampleSpecification = { + /** location on the map, a string in the format "z/lat/lon" */ + location: string; + /** name of this screenshot, used for the filename */ + name: string; + /** Size in pixels of the clip */ + viewport: { + /** Width of the clip */ + width: number; + /** height of the clip */ + height: number; + }; + /** If true, include the Americana demo map controls in the screenshot */ + controls?: boolean; +}; + +// Load list of locations to take map screenshots +const loadSampleLocations = (filePath: string): SampleSpecification[] => { + const rawData = fs.readFileSync(filePath, "utf8"); + return JSON.parse(rawData); +}; + +const sampleFolder = "./samples"; + +const jsonSampleLocations = process.argv[3] ?? "test/sample_locations.json"; + +console.log(`Loading sample locations from ${jsonSampleLocations}`); + +const browserType = process.argv[2] ?? "chrome"; +console.log(`Using browser type: ${browserType}`); + +const screenshots: SampleSpecification[] = + loadSampleLocations(jsonSampleLocations); + +fs.mkdirSync(sampleFolder, { recursive: true }); + +let browser: Browser; + +switch (browserType) { + case "chrome": + default: + browser = await chromium.launch({ + executablePath: process.env.CHROME_BIN, + args: ["--headless=new"], + }); + break; + case "firefox": + browser = await firefox.launch(); + break; + case "safari": + browser = await webkit.launch(); +} + +const context: BrowserContext = await browser.newContext({ + bypassCSP: true, +}); + +const page = await context.newPage(); + +for (const screenshot of screenshots) { + await page.setViewportSize(screenshot.viewport); + await createImage(screenshot); +} + +async function createImage(screenshot: SampleSpecification) { + const pagePath: string = screenshot.controls ? "" : "bare_map.html"; + + await page.goto( + `http://localhost:1776/${pagePath}#map=${screenshot.location}` + ); + + // Wait for map to load, then wait two more seconds for images, etc. to load. + try { + await page.waitForFunction(() => (window as WindowWithMap).map?.loaded(), { + timeout: 3000, + }); + } catch (e) { + console.log(`Timed out waiting for map load`); + } + + try { + await page.screenshot({ + path: `${sampleFolder}/${screenshot.name}.png`, + type: "png", + }); + console.log(`Created ${sampleFolder}/${screenshot.name}.png`); + } catch (err) { + console.error(err); + } +} + +await browser.close(); diff --git a/scripts/generate_style.js b/scripts/generate_style.js index 4c37dac79..3f097415c 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -22,7 +22,7 @@ let opts = program.opts(); let style = Style.build( config.OPENMAPTILES_URL, "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", - "https://zelonewolf.github.io/openstreetmap-americana/fonts/{fontstack}/{range}.pbf", + "https://osm-americana.github.io/fontstack66/{fontstack}/{range}.pbf", opts.locales ); diff --git a/scripts/icon_grid.ts b/scripts/icon_grid.ts new file mode 100644 index 000000000..72996e16b --- /dev/null +++ b/scripts/icon_grid.ts @@ -0,0 +1,73 @@ +import sharp from "sharp"; +import fs from "fs"; +import path from "path"; + +const svgFilename = process.argv[2]; +const outputFilenameBase = path.parse(svgFilename).name; +const outputFilename = `${outputFilenameBase}_preview.png`; +const scale = 40; + +// Function to generate a pixel grid +function generateGridPattern(xOffset: number, yOffset: number): Buffer { + const gridSvg = ` + + + + `; + return Buffer.from(gridSvg); +} + +// Convert and scale SVG +async function convertAndScaleSVG(svgFilename: string): Promise { + const svgBuffer = fs.readFileSync(svgFilename); + + // Get dimensions of the original SVG + const metadata = await sharp(svgBuffer).metadata(); + const width = metadata.width! * scale; + const height = metadata.height! * scale; + + // Resize the SVG + const resizedSvgBuffer = await sharp(svgBuffer, { + density: 72 * scale, + }) + .resize(width, height) + .toBuffer(); + + const xOffset: number = metadata.width! % 2 == 0 ? scale / 2 : 0; + const yOffset: number = metadata.height! % 2 == 0 ? scale / 2 : 0; + + // Generate a pixel grid pattern + const gridPattern = generateGridPattern(xOffset, yOffset); + + // Composite the scaled image over the grid + await sharp({ + create: { + width, + height, + channels: 4, + background: { r: 211, g: 211, b: 211, alpha: 1 }, // Light gray to show white borders + }, + }) + .composite([ + { input: resizedSvgBuffer, blend: "over" }, + { input: gridPattern, tile: true, blend: "over" }, + ]) + .toFile(outputFilename); + console.log(`Wrote ${outputFilename}`); +} + +if (!svgFilename) { + console.error("Please provide an SVG filename."); + process.exit(1); +} + +try { + await convertAndScaleSVG(svgFilename); +} catch (error) { + console.error("Error: ", error); + process.exit(1); +} diff --git a/scripts/object_compare.ts b/scripts/object_compare.ts new file mode 100644 index 000000000..d93461cb4 --- /dev/null +++ b/scripts/object_compare.ts @@ -0,0 +1,145 @@ +/** + * Calculates the difference between two objects by comparing their properties. + * If a property value is an object, it recursively calls itself to calculate the difference. + * Positive numbers for a property means that property is greater in object2 than object1. + * @param {Object|null} object1 - The first object to compare. + * @param {Object|null} object2 - The second object to compare. + * @returns {Object} - An object containing the differences between object2 and object1. + */ +export function calculateDifference( + object1: object | null, + object2: object | null +): object { + // If one object exists and the other doesn't, return the difference + if (object1 === null && object2 !== null) { + return object2; + } else if (object2 === null && object1 !== null) { + return negate(object1); + } + + const difference = {}; + + // Iterate through each property in object1 + for (const key in object1) { + if (typeof object1[key] === "object" && typeof object2![key] === "object") { + // Recursively calculate the difference for nested objects + difference[key] = calculateDifference(object1[key], object2![key]); + } else if ( + typeof object1[key] === "number" && + typeof object2![key] === "number" + ) { + // Calculate the difference for numeric properties + difference[key] = object2![key] - object1[key]; + } else { + // If the property exists in object1 but not in object2, include it in the result + difference[key] = negate(object1![key]); + } + } + + // Include properties that exist in object2 but not in object1 + for (const key in object2!) { + if (!(key in object1!)) { + difference[key] = object2[key]; + } + } + + return difference; +} + +/** + * Negate all numeric properties of this object. + * @param {Object} object - The object to process. + */ +function negate(object: object) { + if (typeof object === "number") { + return -object; + } + + // Create a new object to store the result + const result = {}; + + for (const key in object) { + if (typeof object[key] === "object" && object[key] !== null) { + // If the property value is an object (and not null), recursively process it + result[key] = negate(object[key]); + } else if (typeof object[key] === "number") { + // If the property value is a number, multiply it by the multiplier + result[key] = -object[key]; + } + } + + return result; +} + +// "| | main | this PR | change | % change |", +export type ComparedStats = { + name: string; + beforeValue: number | null; + afterValue: number | null; + change: number; + pctChange: number | null; +}; + +export function statsComparisonRow( + name: string, + val1: number | null, + val2: number | null, + change: number +): ComparedStats { + let pctChange: number | null; + + if (val1 !== null) { + if (val2 !== null) { + pctChange = change / val1; + } else { + pctChange = -1; + } + } else { + pctChange = null; + } + + return { + name, + beforeValue: val1, + afterValue: val2, + change, + pctChange, + }; +} + +const pctFormat: Intl.NumberFormatOptions = { + style: "percent", + minimumFractionDigits: 1, + maximumFractionDigits: 1, + signDisplay: "exceptZero", +}; + +function naLocString(val: number | null) { + return val !== null ? val.toLocaleString("en") : "N/A"; +} + +/** + * produce a markdown row of statistics comparison + */ +export function mdStringValues(stats: ComparedStats): string[] { + const beforeValueStr = naLocString(stats.beforeValue); + const afterValueStr = naLocString(stats.afterValue); + const changeStr = naLocString(stats.change); + const pctChangeStr = + stats.pctChange !== null + ? stats.pctChange.toLocaleString("en", pctFormat) + : "N/A"; + + return [stats.name, beforeValueStr, afterValueStr, changeStr, pctChangeStr]; +} + +export function mdCompareRow( + name: string, + val1: number | null, + val2: number | null, + change: number +): string { + return mdStringValues(statsComparisonRow(name, val1, val2, change)).join( + " | " + ); +} diff --git a/scripts/serve.js b/scripts/serve.js deleted file mode 100644 index 503feb30b..000000000 --- a/scripts/serve.js +++ /dev/null @@ -1,13 +0,0 @@ -import open from "open"; - -import { buildContext } from "./build.js"; - -const port = 1776; - -const context = await buildContext({ - define: { "window.LIVE_RELOAD": "true" }, -}); -await context.watch(); -await context.serve({ servedir: "dist", host: "::", port }); - -open(`http://localhost:${port}/`); diff --git a/scripts/serve.ts b/scripts/serve.ts new file mode 100644 index 000000000..32527e892 --- /dev/null +++ b/scripts/serve.ts @@ -0,0 +1,16 @@ +import { BuildContext } from "esbuild"; +import open from "open"; + +import { buildContext } from "./build"; + +const port = 1776; + +const [mainContext, shieldLibContext]: BuildContext[] = await buildContext({ + define: { "window.LIVE_RELOAD": "true" }, +}); + +await mainContext.watch(); +await shieldLibContext.watch(); +await mainContext.serve({ servedir: "dist", host: "::", port }); + +open(`http://localhost:${port}/`); diff --git a/scripts/sprites.js b/scripts/sprites.js index 6e509ec66..367784da4 100644 --- a/scripts/sprites.js +++ b/scripts/sprites.js @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; -import glob from "glob"; +import { glob } from "glob"; import { Sprites } from "@basemaps/sprites"; await fs.mkdir("./dist/sprites/", { recursive: true }); @@ -11,8 +11,8 @@ const sprites = await Promise.all( await glob("./icons/*.svg") ).map(async (spritePath) => { const id = path.parse(spritePath).name; - const svg = await fs.readFile(spritePath); - return { id, svg }; + const buffer = await fs.readFile(spritePath); + return { id, buffer }; }) ); diff --git a/scripts/stats.js b/scripts/stats.js index f337496a3..3644eb238 100644 --- a/scripts/stats.js +++ b/scripts/stats.js @@ -1,93 +1,184 @@ import * as Style from "../src/js/style.js"; import config from "../src/config.js"; -import { Command } from "commander"; +import { Command, Option } from "commander"; +import fs from "node:fs"; +import zlib from "node:zlib"; const program = new Command(); program - .option("-a, --all-layers", "summary layer stats") - .option("-c, --layer-count", "count number of layers") - .option("-s, --layer-size", "size of all layers") - .option("-l, --layer ", "stats about one layer") + .addOption( + new Option("-a, --all-layers", "summary layer stats") + .conflicts("layerCount") + .conflicts("layerSize") + .conflicts("allJson") + ) + .addOption( + new Option("-c, --layer-count", "count number of layers") + .conflicts("layerSize") + .conflicts("allJson") + ) + .addOption( + new Option("-s, --layer-size", "size of all layers").conflicts("allJson") + ) + .addOption( + new Option( + "-ss1, --spritesheet-1x-size", + "size of 1x sprite sheet" + ).conflicts("allJson") + ) + .addOption( + new Option( + "-ss2, --spritesheet-2x-size", + "size of 2x sprite sheet" + ).conflicts("allJson") + ) + .addOption( + new Option("-sh, --shield-json-size", "size of ShieldJSON").conflicts( + "allJson" + ) + ) + .addOption( + new Option( + "-gsh, --gzip-shield-json-size", + "gzip compressed size of ShieldJSON" + ).conflicts("allJson") + ) + .addOption( + new Option( + "-gs, --gzip-style-size", + "gzip compressed size of style" + ).conflicts("allJson") + ) .option("-loc, --locales ", "language codes", ["mul"]) + .option("-j, --all-json", "output all stats in JSON") .option("-pp, --pretty", "pretty-print JSON output") .option( - "-pg, --print-group ", - "print a list of the layers in a group" - ) - .option("-pl, --print-layer ", "print the JSON of a layer"); + "-d, --directory ", + "specify location of Americana distribution", + ["dist"] + ); + program.parse(process.argv); -let opts = program.opts(); +const opts = program.opts(); if (Object.keys(opts).length === 1) program.help(); -let style = Style.build( +const locales = opts.locales[0].split(","); +const distDir = opts.directory; + +const style = Style.build( config.OPENMAPTILES_URL, "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", - "https://zelonewolf.github.io/openstreetmap-americana/fonts/{fontstack}/{range}.pbf", - opts.locales + "https://osm-americana.github.io/fontstack66/{fontstack}/{range}.pbf", + locales ); const layers = style.layers; const layerCount = layers.length; -const layerSize = JSON.stringify(layers).length; -const layerMap = new Map(); -const layerGroupMap = new Map(); -const layerSizeStats = new Map(); -const layerGroupSizeStats = new Map(); -const layerGroupCountStats = new Map(); +if (opts.layerCount) { + console.log(layerCount); + process.exit(); +} -for (let i = 0; i < layers.length; i++) { - let layer = layers[i]; - layerMap.set(layer.id, layers[i]); - let layerSize = JSON.stringify(layer).length; - let layerGroup = layer.id.split("_", 1)[0]; - layerSizeStats.set(layer.id, JSON.stringify(layer).length); - if (!layerGroupSizeStats.has(layerGroup)) { - layerGroupSizeStats.set(layerGroup, layerSize); - layerGroupCountStats.set(layerGroup, 1); - layerGroupMap.set(layerGroup, [layer.id]); - } else { - layerGroupSizeStats.set( - layerGroup, - layerGroupSizeStats.get(layerGroup) + layerSize - ); - layerGroupCountStats.set( - layerGroup, - layerGroupCountStats.get(layerGroup) + 1 - ); - layerGroupMap.get(layerGroup).push(layer.id); - } +function spriteSheetSize(distDir, single) { + let size = single ? "" : "@2x"; + return ( + fs.statSync(`${distDir}/sprites/sprite${size}.png`).size + + fs.statSync(`${distDir}/sprites/sprite${size}.json`).size + ); } -if (opts.layerCount) { - console.log(`${layerCount} layers`); +function distFileSize(distDir, path) { + return fs.statSync(`${distDir}/${path}`).size; } -if (opts.layerSize) { - console.log(`Total layer size ${layerSize.toLocaleString("en-US")} bytes`); +function gzipSize(content) { + return zlib.gzipSync(content).length; } -if (opts.allLayers) { - console.log(`${layerCount} layers, grouped as follows:`); - layerGroupSizeStats.forEach((v, k) => { - let layerCount = layerGroupCountStats.get(k); - let layerString = `${k}(${layerCount})`.padEnd(30, "."); - console.log( - `${layerString}${v.toLocaleString("en-US").padStart(10, ".")} bytes` - ); - }); +const spriteSheet1xSize = spriteSheetSize(distDir, true); +if (opts.spritesheet1xSize) { + console.log(spriteSheet1xSize); + process.exit(); +} + +const spriteSheet2xSize = spriteSheetSize(distDir, false); +if (opts.spritesheet2xSize) { + console.log(spriteSheet2xSize); + process.exit(); } -if (opts.printGroup) { - layerGroupMap.get(opts.printGroup).forEach((lyr) => console.log(lyr)); +const shieldJSONPath = `${distDir}/shields.json`; +const shieldJSONSize = distFileSize(distDir, "shields.json"); +if (opts.shieldJsonSize) { + console.log(shieldJSONSize); + process.exit(); } -if (opts.printLayer) { - if (opts.pretty) { - console.log(JSON.stringify(layerMap.get(opts.printLayer), null, 2)); +const shieldJSONContent = fs.readFileSync(shieldJSONPath, "utf8"); +const gzipShieldJSONSize = gzipSize(shieldJSONContent); +if (opts.gzipShieldJsonSize) { + console.log(gzipShieldJSONSize); + process.exit(); +} + +const styleContent = JSON.stringify(layers); +const styleSize = styleContent.length; +if (opts.layerSize) { + console.log(styleSize); + process.exit(); +} + +const gzipStyleSize = gzipSize(styleContent); +if (opts.gzipStyleSize) { + console.log(gzipStyleSize); + process.exit(); +} + +const layerMap = new Map(); + +const stats = { + layerCount, + styleSize, + gzipStyleSize, + layerGroup: {}, + spriteSheet1xSize, + spriteSheet2xSize, + shieldJSONSize, + gzipShieldJSONSize, +}; + +for (let i = 0; i < layerCount; i++) { + const layer = layers[i]; + layerMap.set(layer.id, layers[i]); + const layerSize = JSON.stringify(layer).length; + const layerGroup = layer["source-layer"] || layer.source || layer.type; + if (stats.layerGroup[layerGroup]) { + stats.layerGroup[layerGroup].size += layerSize; + stats.layerGroup[layerGroup].layerCount++; } else { - console.log(JSON.stringify(layerMap.get(opts.printLayer))); + stats.layerGroup[layerGroup] = { + size: layerSize, + layerCount: 1, + }; + } +} + +if (opts.allJson) { + process.stdout.write(JSON.stringify(stats, null, opts.pretty ? 2 : null)); + process.exit(); +} + +if (opts.allLayers) { + for (const layerGroup in stats.layerGroup) { + let layerStats = stats.layerGroup[layerGroup]; + let layerString = `${layerGroup}(${layerStats.layerCount})`.padEnd(30, "."); + console.log( + `${layerString}${layerStats.size + .toLocaleString("en-US") + .padStart(10, ".")} bytes` + ); } } diff --git a/scripts/stats_compare.js b/scripts/stats_compare.js new file mode 100644 index 000000000..9e620fb09 --- /dev/null +++ b/scripts/stats_compare.js @@ -0,0 +1,124 @@ +import { calculateDifference, mdCompareRow } from "./object_compare"; + +const stats1 = JSON.parse(process.argv[2]); +const stats2 = JSON.parse(process.argv[3]); + +const difference = calculateDifference(stats1, stats2); + +const diffHeaderRow = [ + "| | main | this PR | change | % change |", + "|-----------|--------------:|-------------:|----------------:|----------------:|", +]; + +/** + * Show comparison of overall aggregate statistics between this PR and previous + */ + +const layersRow = mdCompareRow( + "Layers", + stats1.layerCount, + stats2.layerCount, + difference.layerCount +); + +const sizeRow = mdCompareRow( + "StyleJSON Size (b)", + stats1.styleSize, + stats2.styleSize, + difference.styleSize +); + +const gzSizeRow = mdCompareRow( + "Compressed StyleJSON Size (b)", + stats1.gzipStyleSize, + stats2.gzipStyleSize, + difference.gzipStyleSize +); + +const shieldRow = mdCompareRow( + "ShieldJSON Size (b)", + stats1.shieldJSONSize, + stats2.shieldJSONSize, + difference.shieldJSONSize +); + +const gzShieldRow = mdCompareRow( + "Compressed ShieldJSON Size (b)", + stats1.gzipShieldJSONSize, + stats2.gzipShieldJSONSize, + difference.gzipShieldJSONSize +); + +const ss1xRow = mdCompareRow( + "1x Sprite Sheet Size (b)", + stats1.spriteSheet1xSize, + stats2.spriteSheet1xSize, + difference.spriteSheet1xSize +); + +const ss2xRow = mdCompareRow( + "2x Sprite Sheet Size (b)", + stats1.spriteSheet2xSize, + stats2.spriteSheet2xSize, + difference.spriteSheet2xSize +); + +printTable("Style size statistics", [ + layersRow, + sizeRow, + gzSizeRow, + ss1xRow, + ss2xRow, + shieldRow, + gzShieldRow, +]); + +/** + * Show comparison of the number of layers in each group before and after + */ + +const layerCountChangeRows = []; + +for (const layer in difference.layerGroup) { + layerCountChangeRows.push( + mdCompareRow( + layer, + stats1.layerGroup[layer]?.layerCount, + stats2.layerGroup[layer]?.layerCount, + difference.layerGroup[layer]?.layerCount + ) + ); +} + +printTable("Layer count comparison", layerCountChangeRows); + +/** + * Show comparison of the aggregate size of layers in each group before and after + */ + +const layerSizeChangeRows = []; + +for (const layer in difference.layerGroup) { + layerSizeChangeRows.push( + mdCompareRow( + layer, + stats1.layerGroup[layer]?.size, + stats2.layerGroup[layer]?.size, + difference.layerGroup[layer]?.size + ) + ); +} + +printTable("Layer size comparison", layerSizeChangeRows); + +function printTable(headingText, rows) { + const table = [...diffHeaderRow, ...rows].join("\n"); + const text = ` + +## ${headingText} + +${table} +`; + + console.log(text); +} diff --git a/scripts/status_map.js b/scripts/status_map.js index 256bccc8f..ba50f5327 100644 --- a/scripts/status_map.js +++ b/scripts/status_map.js @@ -24,7 +24,7 @@ let worldSVG = fs.readFileSync(`${process.cwd()}/scripts/blank_map_world.svg`, { }); worldSVG = fillPaths( worldSVG, - Object.keys(shields) + Object.keys(shields.networks) .map((network) => network.match(/^(\w\w)(?::|$)|^omt-(\w\w)-/)) .filter((m) => m) .map((m) => m[1] || m[2]) diff --git a/scripts/taginfo.js b/scripts/taginfo.js index f3df82846..a0053ee2a 100644 --- a/scripts/taginfo.js +++ b/scripts/taginfo.js @@ -2,27 +2,36 @@ import * as fs from "fs"; import * as ShieldDef from "../src/js/shield_defs.js"; -import * as Shields from "../src/js/shield.js"; -import * as Gfx from "../src/js/screen_gfx.js"; -import * as CustomShields from "../src/js/custom_shields.js"; -import { Canvas } from "canvas"; import namer from "color-namer"; import { mkdir } from "node:fs/promises"; +import { + ShieldRenderer, + InMemorySpriteRepository, +} from "@americana/maplibre-shield-generator"; +/** + * TODO - BUG + * Exporting HeadlessGraphicsFactory in shieldlib's index.ts causes an unexplained error in node-canvas + * where it thinks it's running in a browser and attempts to create a browser canvas, and fails. The same + * behavior occurs in skia-canvas. This hack works well enough for the taginfo script, but ideally the shield + * library should be capable of generating shields outside the browser. + */ +import { HeadlessGraphicsFactory } from "@americana/maplibre-shield-generator/src/headless_graphics"; +import { + routeParser, + shieldPredicate, + networkPredicate, +} from "../src/js/shield_format"; await mkdir("dist/shield-sample", { recursive: true }); -//Headless graphics context -Gfx.setGfxFactory((bounds) => { - let canvas = new Canvas(bounds.width, bounds.height, "svg"); - let ctx = canvas.getContext("2d"); - ctx.imageSmoothingQuality = "high"; - ctx.textAlign = "center"; - ctx.textBaseline = "top"; - ctx.canvas = canvas; - return ctx; -}); - const shieldGfxMap = new Map(); +const shields = ShieldDef.loadShields(); + +const shieldRenderer = new ShieldRenderer(shields, routeParser) + .filterImageID(shieldPredicate) + .filterNetwork(networkPredicate) + .graphicsFactory(new HeadlessGraphicsFactory("svg")) + .renderOnRepository(new InMemorySpriteRepository()); const colorNames = new Map(); @@ -49,46 +58,36 @@ function getNamedColor(colorString, defaultColor) { * @param {*} project - The project description object to modify. */ function addNetworkTags(project) { - CustomShields.loadCustomShields(); - let shields = ShieldDef.loadShields(); + let shieldSpec = shields; // Convert each shield's rendering metadata to an entry that taginfo understands. - let tags = Object.entries(shields) + let tags = Object.entries(shieldSpec.networks) .filter((entry) => !entry[0].match(/^omt-/)) .map((entry) => { let network = entry[0], - definition = entry[1]; + shieldDef = entry[1]; - let icon = definition.spriteBlank || definition.noref?.spriteBlank; + let icon = shieldDef.spriteBlank || shieldDef.noref?.spriteBlank; if (Array.isArray(icon)) { icon = icon[0]; } let icon_url; + let defBanners = shieldDef.banners; - //Shield ID with ref as a single space character - let id = `shield\n${network}= `; - - let routeDef = Shields.getRouteDef(id); - let shieldDef = Shields.getShieldDef(routeDef); - - if (icon == undefined && shieldDef.canvasDrawnBlank !== undefined) { - //Generate empty canvas sized to the graphic - let shieldGfx = Gfx.getGfxContext( - Shields.getDrawnShieldBounds(shieldDef, " ") - ); - - //Draw shield to the canvas - Shields.drawShield(shieldGfx, shieldDef, routeDef); + if (icon == undefined && shieldDef?.shapeBlank !== undefined) { + //Remove banners and allow blank to render without a ref + delete shieldDef.banners; + shieldDef.notext = true; - delete shields[network].modifiers; - let def = JSON.stringify(shields[network]); + let shieldGfx = shieldRenderer.getGraphicForRoute(network, "", ""); - if (!shieldGfxMap.has(def)) { - shieldGfxMap.set(def, shieldGfxMap.size); + let shieldDefText = JSON.stringify(shieldDef); + if (!shieldGfxMap.has(shieldDefText)) { + shieldGfxMap.set(shieldDefText, shieldGfxMap.size); } - let network_filename_id = shieldGfxMap.get(def); + let network_filename_id = shieldGfxMap.get(shieldDefText); let save_filename = `dist/shield-sample/shield_${network_filename_id}.svg`; if (!fs.existsSync(save_filename)) { @@ -110,8 +109,8 @@ function addNetworkTags(project) { svgText = svgText.replace(/#fff/gi, shieldDef.colorDarken); } - delete shields[network].modifiers; - let def = JSON.stringify(shields[network]); + delete shieldSpec.networks[network].banners; + let def = JSON.stringify(shieldSpec.networks[network]); if (!shieldGfxMap.has(def)) { shieldGfxMap.set(def, shieldGfxMap.size); @@ -129,8 +128,8 @@ function addNetworkTags(project) { } let description = `Roads carrying routes in this network are marked by `; - if (definition.canvasDrawnBlank) { - let shapeDef = definition.canvasDrawnBlank; + if (shieldDef.shapeBlank) { + let shapeDef = shieldDef.shapeBlank; let prettyShapeName = `${shapeDef.drawFunc}-shaped`; let prettyFillColor = getNamedColor(shapeDef.params.fillColor, "white"); let prettyStrokeColor = getNamedColor( @@ -139,12 +138,11 @@ function addNetworkTags(project) { ); switch (shapeDef.drawFunc) { + case "pill": + prettyShapeName = "pill-shaped"; + break; case "roundedRectangle": - if (shapeDef.params.radius == 10) { - prettyShapeName = "pill-shaped"; - } else { - prettyShapeName = "rectangular"; - } + prettyShapeName = "rectangular"; break; case "hexagonVertical": prettyShapeName = "vertical hexagonal"; @@ -171,10 +169,8 @@ function addNetworkTags(project) { description += "shields"; } - if (definition.modifiers && definition.modifiers.length > 0) { - description += ` modified by ${definition.modifiers.join( - ", " - )} banners`; + if (defBanners && defBanners.length > 0) { + description += ` modified by ${defBanners.join(", ")} banners`; } description += "."; diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index 6ce096d9b..672adcccb 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -383,6 +383,14 @@ "description": "Intermittent lakes are translucent with a dashed line representing the lakeshore.", "doc_url": "https://openmaptiles.org/schema/#water" }, + { + "key": "amenity", + "value": "fuel", + "object_types": ["node", "area"], + "description": "Fuel stations are marked by an icon representing a gas pump.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_fuel.svg" + }, { "key": "amenity", "value": "school", @@ -391,6 +399,14 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_school.svg" }, + { + "key": "amenity", + "value": "taxi", + "object_types": ["node", "area"], + "description": "Taxi stands are marked by an icon representing the front view of a taxi cab.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_taxi.svg" + }, { "key": "amenity", "value": "kindergarten", @@ -403,17 +419,17 @@ "key": "amenity", "value": "college", "object_types": ["node", "area"], - "description": "Schools are marked by an icon representing a schoolhouse with a triangular flag raised above it.", + "description": "Colleges are marked by an icon representing a mortarboard cap.", "doc_url": "https://openmaptiles.org/schema/#poi", - "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_school.svg" + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_mortarboard.svg" }, { "key": "amenity", "value": "university", "object_types": ["node", "area"], - "description": "Schools are marked by an icon representing a schoolhouse with a triangular flag raised above it.", + "description": "Colleges are marked by an icon representing a mortarboard cap.", "doc_url": "https://openmaptiles.org/schema/#poi", - "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_school.svg" + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_mortarboard.svg" }, { "key": "amenity", @@ -455,6 +471,14 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_health_cross.svg" }, + { + "key": "amenity", + "value": "police", + "object_types": ["node", "area"], + "description": "Police stations are marked by an icon representing a police badge.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_police_shield.svg" + }, { "key": "amenity", "value": "townhall", @@ -463,6 +487,46 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_town_hall.svg" }, + { + "key": "amenity", + "value": "post_office", + "object_types": ["node", "area"], + "description": "Post offices are marked by an icon representing an envelope.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_envelope.svg" + }, + { + "key": "tourism", + "value": "guest_house", + "object_types": ["node", "area"], + "description": "Guest houses are marked by an icon representing a person laying in a bed.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_hotel.svg" + }, + { + "key": "tourism", + "value": "hostel", + "object_types": ["node", "area"], + "description": "Hostels are marked by an icon representing two people laying in a bunk bed.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_hostel.svg" + }, + { + "key": "tourism", + "value": "hotel", + "object_types": ["node", "area"], + "description": "Hotels are marked by an icon representing a person laying in a bed.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_hotel.svg" + }, + { + "key": "tourism", + "value": "motel", + "object_types": ["node", "area"], + "description": "Motels are marked by an icon representing a person laying in a bed.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_hotel.svg" + }, { "key": "tourism", "value": "museum", @@ -471,7 +535,6 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_museum.svg" }, - { "key": "amenity", "value": "clinic", @@ -480,6 +543,14 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_health_cross.svg" }, + { + "key": "amenity", + "value": "library", + "object_types": ["node", "area"], + "description": "Libraries are marked by an icon representing a book.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_book_upright.svg" + }, { "key": "amenity", "value": "parking", @@ -488,6 +559,14 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_p.svg" }, + { + "key": "amenity", + "value": "place_of_worship", + "object_types": ["node", "area"], + "description": "Places of worship are marked by a square representing a building, topped with a finial representing the religion for common religions.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_generic.svg" + }, { "key": "amenity", "value": "bus_station", @@ -520,6 +599,14 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_rail.svg" }, + { + "key": "aerialway", + "value": "station", + "object_types": ["node", "area"], + "description": "Aerialway stations are marked by an icon depicting an aerial tramway car.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_aerialway_circle.svg" + }, { "key": "railway", "value": "tram_stop", @@ -536,6 +623,102 @@ "doc_url": "https://openmaptiles.org/schema/#poi", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_rail.svg" }, + { + "key": "religion", + "value": "buddhist", + "object_types": ["node", "area"], + "description": "Buddhist places of worship are marked by an icon representing a building topped by a dharmachakra.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_buddhist.svg" + }, + { + "key": "religion", + "value": "christian", + "object_types": ["node", "area"], + "description": "Christian places of worship are marked by an icon representing a building topped by a cross.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_christian.svg" + }, + { + "key": "religion", + "value": "hindu", + "object_types": ["node", "area"], + "description": "Hindu places of worship are marked by an icon representing a building topped by an om symbol.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_hindu.svg" + }, + { + "key": "religion", + "value": "jewish", + "object_types": ["node", "area"], + "description": "Jewish places of worship are marked by an icon representing a building topped by a star of David.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_jewish.svg" + }, + { + "key": "religion", + "value": "muslim", + "object_types": ["node", "area"], + "description": "Muslim places of worship are marked by an icon representing a building topped by a star and crescent.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_muslim.svg" + }, + { + "key": "religion", + "value": "sikh", + "object_types": ["node", "area"], + "description": "Sikh places of worship are marked by an icon representing a building topped by a khanda.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_sikh.svg" + }, + { + "key": "religion", + "value": "shinto", + "object_types": ["node", "area"], + "description": "Shinto places of worship are marked by an icon representing a building topped by a torii.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_shinto.svg" + }, + { + "key": "religion", + "value": "taoist", + "object_types": ["node", "area"], + "description": "Taoist places of worship are marked by an icon representing a building topped by a yin-yang symbol.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_pow_taoist.svg" + }, + { + "key": "shop", + "value": "books", + "object_types": ["node", "area"], + "description": "Bookstores are marked by an icon representing a book.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_book_upright.svg" + }, + { + "key": "shop", + "value": "car", + "object_types": ["node", "area"], + "description": "Car dealerships are marked by an icon representing a front facing vehicle.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_car_shop.svg" + }, + { + "key": "shop", + "value": "car_repair", + "object_types": ["node", "area"], + "description": "Car mechanic shops are marked by an icon representing a front facing vehicle with a wrench above it.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_car_repair.svg" + }, + { + "key": "shop", + "value": "supermarket", + "object_types": ["node", "area"], + "description": "Supermarkets are marked by an icon representing a grocery cart.", + "doc_url": "https://openmaptiles.org/schema/#poi", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_supermarket.svg" + }, { "key": "highway", "value": "motorway", diff --git a/shieldlib/.mocharc.yml b/shieldlib/.mocharc.yml new file mode 100644 index 000000000..4df82a721 --- /dev/null +++ b/shieldlib/.mocharc.yml @@ -0,0 +1,6 @@ +recursive: true +extension: + - ts +spec: + - test/spec/*.ts +import: tsx diff --git a/shieldlib/.prettierignore b/shieldlib/.prettierignore new file mode 100644 index 000000000..9d0b71a3c --- /dev/null +++ b/shieldlib/.prettierignore @@ -0,0 +1,2 @@ +build +dist diff --git a/shieldlib/README.md b/shieldlib/README.md new file mode 100644 index 000000000..fcadf3237 --- /dev/null +++ b/shieldlib/README.md @@ -0,0 +1,326 @@ +# Americana Shield Renderer + +The Americana shield renderer is a library intended to draw highway shields on a [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js) vector-tiled map. + +![Pictoral highway shields](https://wiki.openstreetmap.org/w/images/6/6d/Rendered_shields_americana.png) + +## Shield rendering workflow + +Rendering shields requires the following compoments: + +1. **Encode shield information in vector tiles**. First, your tiles must contain the information which tells the shield renderer what shields to draw. In OpenMapTiles, shield information is encoded in the [`transportation_name`](https://openmaptiles.org/schema/#transportation_name) vector tile layer with a series of attributes named `route_1`, `route_2`, etc. Each attribute contains a text string which contains all of the information needed to determine which graphic to display, including numeric route number if the shield is numbered. However, this library allows you to specify how the shield information has been encoded, and it's possible to stitch together data from multiple fields when encoding shield data. + +2. **Expose shield information in a style layer**. Next, route information must be exposed in a maplibre expression using [image](https://maplibre.org/maplibre-gl-js-docs/style-spec/expressions/#types-image) in a structured string containing the route information. For example, you might encode Interstate 95 as an image named `shield|US:I=95`. Normally, the image expression is used to point to pre-designated sprites in a sprite sheet, but in this case, we're pointing to a sprite which doesn't exist called `shield|US:I=95`. This will trigger a `styleimagemissing` event which allows the shield renderer to create the required graphic on the fly. As an example of how to encode shield information, see OSM Americana's [`highway_shield`](https://github.com/ZeLonewolf/openstreetmap-americana/blob/main/src/layer/highway_shield.js) style layer. + +3. **Define a parser that describes how route information is encoded**. There are three parts to a route definition: + + 1. The `network` string, which defines a network with a common shield shape, graphic, and color + 1. The `ref` string, which defines a text sequence that should be drawn on top of the shield graphic + 1. The `name` string, which defines a name, separate from the ref, that is used to determine which graphic to draw + + ```typescript + let routeParser = { + //format is `shield|${network}=${ref}|${name}` + parse: (id: string) => { + let id_parts = id.split("|"); + let network_ref = id_parts[1].split("="); + + return { + network: network_ref[0], + ref: network_ref[1], + name: id_parts[2], + }; + }, + format: (network: string, ref: string, name: string) => + `shield|${network}=${ref}|${name}`, + }; + ``` + +4. **(Optional) Create predicates that define which shields will be handled**. For example, if all sprite IDs in your style that need a shield begin with the string `shield|`, this would look like: + + ```typescript + let shieldPredicate = (imageID: string) => imageID.startsWith("shield"); + ``` + + This step can be skipped if all unhandled image IDs are shields. + + Additionally, you can specify which networks will be handled. The example below ignores all `nwn`, `lwn`, `ncn`, etc. network values: + + ```typescript + let networkPredicate = (network: string) => + !/^[lrni][chimpw]n$/.test(network); + ``` + +5. **Create shield definitions and artwork**. The shield definition is expressed as a JSON file along with a set of sprites containing any raster artwork used for the shields. It can be generated as an object or hosted as a JSON file accessible by URL. See the next section for how to create this definition. + +6. **Hook up the shield generator to a maplibre-gl-js map**. Pass either the URL of the JSON shield definition or create an object in javascript code. There are two separate classes for each approach. + + ```typescript + new URLShieldRenderer("shields.json", routeParser) + .filterImageID(shieldPredicate) + .filterNetwork(networkPredicate) + .renderOnMaplibreGL(map); + ``` + + ```typescript + new ShieldRenderer(shields, routeParser) + .filterImageID(shieldPredicate) + .filterNetwork(networkPredicate) + .renderOnMaplibreGL(map); + ``` + +## Shield Definition + +The purpose of the shield definition is to define which graphics and text to draw for each network/ref/name combination that you wish to display. This can be created in javascript as an object, or as an HTTP-accessible JSON file. + +This description uses the following conventions: + +- A **network** is a set of routes with a _common graphical presentation_. Each route may have variations in appearance, such as a different route number, or a special case definition as described below. The network value corresponds in concept to the OpenStreetMap [network tag](https://wiki.openstreetmap.org/wiki/Key:network). +- A **ref** contains the text to be drawn on shield artwork. The ref value corresponds in concept to the OpenStreetMap [ref tag](https://wiki.openstreetmap.org/wiki/Key:ref). + +The structure is as follows: + +```json +{ + "options": { + "bannerHeight": 9, + "bannerPadding": 1, + "bannerTextColor": "black", + "bannerTextHaloColor": "white", + "shieldFont": "'Noto Sans Condensed', sans-serif-condensed, 'Arial Narrow', sans-serif", + "shieldSize": 20 + }, + "default": { ...definition }, + "network_1": { ...definition }, + "network_2": { ...definition }, + "network_3": { ...definition } +} + +``` + +The options block contains global parameters that apply across all shield drawing: + +- **`bannerHeight`**: height of each text banner +- **`bannerPadding`**: padding between each banner +- **`bannerTextColor`**: color to draw text banners above the shield +- **`bannerTextHaloColor`**: color to draw an outline around the text banner +- **`shieldFont`**: ordered list of fonts to use for shield text and banners. This project uses Noto Sans Condensed by default, hosted as a downloadable web font on [another repository](https://github.com/osm-americana/web-fonts). +- **`shieldSize`**: "standard" size to use for shields in 1x pixels. However, some shields may diverge, for example, drawn diamond shields are drawn slightly larger for visual similarity with squares. + +You should create one definition entry for each network. The entry key must match the encoded `network` value exactly. The "default" network defines what should be drawn if there's no definition for a particular network. A network definiton can contain any combination of the following parameters: + +```json +{ + "textColor": "black", + "textHaloColor": "white", + "padding": { + "left": 3, + "right": 3, + "top": 3, + "bottom": 3 + }, + "spriteBlank": ["name_of_image_2", "name_of_image_3", "name_of_image_4"], + "noref": { + "spriteBlank": "name_of_image_noref" + } + "shapeBlank": { + "drawFunc": "pentagon", + "params": { + "pointUp": false, + "yOffset": 5, + "sideAngle": 0, + "fillColor": "white", + "strokeColor": "black", + "radius1": 2, + "radius2": 2 + } + }, + "banners": ["ALT"], + "bannerTextColor": "#000", + "bannerTextHaloColor": "#FFF", + "textLayout": { + "constraintFunc": "roundedRect", + "options": { + "radius": 2 + } + }, + "colorLighten": "#006747", + "overrideByRef": { + "REF": { + "spriteBlank": "special_case_image", + "textColor": "#003f87", + "colorLighten": "#003f87" + } + }, + "refsByName": { + "Audubon Parkway": "AU" + }, + "overrideByName": { + "Merritt Parkway": { + "spriteBlank": "shield_us_ct_parkway_merritt" + } + } +} +``` + +### Shield property descriptions + +- **`textColor`**: determines what color to draw the `ref` on the shield. +- **`textHaloColor`**: color to draw a knockout halo around the `ref` text. +- **`padding`**: padding around the `ref`, which allows you to squeeze the text into a smaller space within the shield. +- **`spriteBlank`**: specify the name of an image in the sprite sheet to use as the shield background. This can either be a single string or an array of strings if there are multiple options for different width. + - If `spriteBlank` is an array of strings, they must be ordered from narrowest to widest, and the filenames must be suffixed with a consecutive range of integers, representing the optimal number of characters to display in each icon. +- **`noref`**: specify alternate attributes to apply in the event that no `ref` is supplied. This allows you to use one graphic for numbered routes and a separate unitary graphic for non-numbered routes within the same network. Supports **`spriteBlank`**, **`colorLighten`**, and **`colorDarken`**. +- **`shapeBlank`**: specify that a shield should be drawn as a common shape (rectangle, ellipse, pentagon, etc), with colors and dimensions as specified. See the [drawn shield shapes](#defining-drawn-shield-shapes) section for available drawing options. +- **`banners`**: specify that one or more short text strings (up to 4 characters) should be drawn above the shield. This is specified as an array, and text will be drawn in order from top to bottom. Below is an example of bannered shields with up to three banners: + +![Bannered routes near Downington, PA](https://wiki.openstreetmap.org/w/images/f/f8/Downington_bannered_routes_Americana.png) + +- **`bannerTextColor`**: specify the color of the banner text. +- **`bannerTextHaloColor`**: specify the color of the banner knockout halo. +- **`textLayout`**: specify how text should be inscribed within the padded bounds of the shield. The text will be drawn at the maximum size allowed by this constraint. See the [text layout functions](#text-layout-functions) section for text layout options. +- **`colorLighten`**: specify that the shield artwork should be lightened (multiplied) by the specified color. This means that black areas will be recolor with this color and white areas will remain the same. Alpha values will remain unmodified. +- **`colorDarken`**: specify that the shield artwork should be darkened by the specified color. This means that white areas will be recolor with this color and black areas will remain the same. Alpha values will remain unmodified. +- **`overrideByRef`**: specify that a specific `ref` within a `network` should have different shield properties than other routes in the network, with one entry per special-case `ref`. Supported options are **`spriteBlank`**, **`textColor`**, and **`colorLighten`**. +- **`refsByName`**: specify that a `name` with the specified key should be treated as a `ref` with the specified value. +- **`ref`**: specify that all shields in this network should be drawn with the specified `ref` value. +- **`overrideByName`**: specify that particular `name` should use a specific **`spriteBlank`** which differs from the rest of the network. + +### Handling special case networks + +The shield specification allows for the handling of special cases. For example, in OSM Americana, we wanted to create special shields for the [Kentucky Parkway System](https://en.wikipedia.org/wiki/List_of_parkways_and_named_highways_in_Kentucky). This network used a particularly ugly set of shields that looked something like this: + +![Western Kentucky Parkway shield](https://upload.wikimedia.org/wikipedia/en/d/db/Western_Kentucky_Parkway_fair_use.svg) + +The Americana team wanted to draw a series of shields that used two-letter codes to represent each of the Parkways, so that it would look like this: + +![Kentucky parkways](https://wiki.openstreetmap.org/w/images/d/da/Kentucky_parkway_Americana.png) + +However, these two-letter codes weren't actually used as route numbers on shields, so it wasn't appropriate to add them to the `ref=*` tag in OpenStreetMap. Instead, the team used the `refsByName` property, and then defined what two-letter code is assigned to each named route network as follows: + +```json + "US:KY:Parkway": { + "spriteBlank":"shield_us_ky_parkway", + "textColor":"#003f87", + "padding": { + "left":2, + "right":2, + "top":2, + "bottom":6 + }, + "refsByName": { + "Audubon Parkway":"AU", + "Bluegrass Parkway":"BG", + "Bluegrass Pkwy":"BG", + "Cumberland Parkway":"LN", + "Cumberland Pkwy":"LN", + "Hal Rogers Parkway":"HR", + "Hal Rogers Pkwy":"HR", + "Mountain Parkway":"MP", + "Mountain Pkwy":"MP", + "Purchase Parkway":"JC", + "Purchase Pkwy":"JC", + "Western Kentucky Parkway":"WK", + "Western Kentucky Pkwy":"WK" + } + }, + +``` + +Another special case is when select routes within a network need to be styled differently. For example, [Georgia State Route 520](https://en.wikipedia.org/wiki/Georgia_State_Route_520) is signed with a green-colored shield, while the default color for Georgia state highways is black. The shield assembly below shows an intersection and concurrency with both styles of Georgia state route: + +![Georgia Route 520](https://upload.wikimedia.org/wikipedia/commons/4/4f/GA_green_route_concurrency.png) + +This effect can be achieved by overriding the text and sprite color in the route definition as follows. For multiple special case refs, add multiple entries. + +```json +"US:GA": { + "spriteBlank": ["shield_us_ga_2", "shield_us_ga_3"], + "textColor": "black", + "overrideByRef": { + "520": { + "textColor": "#006747", + "colorLighten": "#006747" + } + } +} +``` + +In this example, the two `shield_us_ga_...` sprite blanks represent the narrow and wide versions of the Georgia state route shield, and are colored black with white fill. This results in the following: + +![Georgia State Route 520 Concurrency](https://wiki.openstreetmap.org/w/images/0/0b/Columbs_georgia_concurrency.png) + +Finally, the last special case is when one _named_ route in a network requires a different shield from other routes in the network. For example, the Merritt Parkway in Connecticut is tagged in OSM as `network=US:CT:Parkway` + `name=Merritt Parkway`. There are also two additional parkways in Connecticut tagged the same way. However, only the Merritt Parkway has a shield. Thus, we can define the `US:CT:Parkway` as an empty definition with an exception for a route named `Merritt Parkway`: + +```json +"US:CT:Parkway": { + "overrideByName" : { + "Merritt Parkway": { + "spriteBlank": "shield_us_ct_parkway_merritt", + }, + } +} +``` + +The clip below shows the result where the Merritt Parkway (concurrent with CT-15) ends and the Wilbur Cross Parkway (name but no shield) begins: + +![Merritt Parkway Shield](https://wiki.openstreetmap.org/w/images/3/37/Ct_parkway_shield.png) + +### Text layout functions + +Text is laid out on shields in accordance with the specified `textLayout` value. The text will be drawn, measured, and expanded until it hits the edge of a text layout constraint. For example, an `ellipse` constraint would fill a padded shield like this, with the text drawn from the center and expanding until it reaches the ellipse: + +![Ellipse constraint](https://upload.wikimedia.org/wikipedia/commons/7/72/Ellipse_constraint.png) + +Not all constraints are center-specified. For example, the `southHalfEllipse` constraint would grow from the top of the shield as follows: + +![South half-ellipse constraint](https://upload.wikimedia.org/wikipedia/commons/6/62/South_half-ellipse_constraint.png) + +The supported text constraints are: + +- `diamond` +- `ellipse` +- `rect` +- `roundedRect` +- `southHalfEllipse` +- `triangleDown` + +### Defining drawn shield shapes + +If `shapeBlank` is specified, the shield will be drawn as a shape. This needs to be specified with a drawing function, `drawFunc` and a `params` block the describes how the shape will be drawn. The draw functions are as follows: + +| | `drawFunc` | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| ![](https://upload.wikimedia.org/wikipedia/commons/c/cf/Diamond_highway_shield_shape.svg) | `diamond` | +| ![](https://upload.wikimedia.org/wikipedia/commons/3/30/Black_and_white_circle_shape_as_used_in_highway_shields.svg) | `ellipse` | +| ![](https://upload.wikimedia.org/wikipedia/commons/7/75/Escutcheon_highway_shield_shape.svg) | `escutcheon` | +| ![](https://upload.wikimedia.org/wikipedia/commons/f/f8/Fishhead_highway_shield_shape.svg) | `fishhead` | +| ![](https://upload.wikimedia.org/wikipedia/commons/6/61/Hexagon_highway_shield_shape.svg) | `hexagonVertical` | +| ![](https://upload.wikimedia.org/wikipedia/commons/2/25/Horizontal_hexagon_shape.svg) | `hexagonHorizontal` | +| ![](https://upload.wikimedia.org/wikipedia/commons/f/f8/Octagon_shield_shape.svg) | `octagonVertical` | +| ![](https://upload.wikimedia.org/wikipedia/commons/f/ff/Pentagon_shield_shape.svg)![](https://upload.wikimedia.org/wikipedia/commons/b/b0/Black_and_white_home_plate_shape_as_used_in_highway_shields.svg) | `pentagon` | +| ![](https://upload.wikimedia.org/wikipedia/commons/archive/1/17/20230326013156%21Pill_shape.svg) | `pill` | +| ![](https://upload.wikimedia.org/wikipedia/commons/3/30/Black_and_white_circle_shape_as_used_in_highway_shields.svg)![](https://upload.wikimedia.org/wikipedia/commons/archive/a/a1/20230326013519%21Rounded_rectangle_shape.svg) | `roundedRectangle` | +| ![](https://upload.wikimedia.org/wikipedia/commons/a/ad/Black_and_white_downward_trapezoid_as_used_in_highway_shields.svg)![](https://upload.wikimedia.org/wikipedia/commons/5/56/Black_and_white_upward_trapezoid_as_used_in_highway_shields.svg) | `trapezoid` | +| ![](https://upload.wikimedia.org/wikipedia/commons/a/ad/Downward_triangle_highway_shield_shape.svg) | `triangle` | + +The following `params` options can be specified: + +- `sideAngle` - indicates angle (in degrees) at which side edges deviate from vertical. Applies to `trapezoid`, `pentagon`, `hexagonHorizontal`, `octagonVertical`. +- `fill` - specifies the internal fill color. +- `yOffset` - indicates height (in pixels) at which the bottom and/or top edges deviate from horizontal. Applies to `escutcheon`, `pentagon`, `hexagonVertical`, `octagonVertical`. +- `outline` - specifies the outline color. +- `outlineWidth` - specifies the width of the outline. +- `pointUp` - applies to several shape types and specifies whether the pointy side is up. +- `radius` - specifies the rounding radius, in pixels, to use for corners. +- `radius1` - Corner radius of pointed side of pentagon (defaults to 2) +- `radius2` - Corner radius of flat side of pentagon (defaults to 0) +- `shortSideUp` - for `trapezoid` only, a boolean which specifies whether the short side is up or down. + +### Custom shield graphics + +In addition to the stock drawing functions, a custom draw function can be specified. `paDot` and `branson` are included as examples of this, for rendering the [Allegheny County belt system](https://en.wikipedia.org/wiki/Allegheny_County_belt_system) and the Branson, Missouri colored route system. See the file `src/custom_shields.mjs` for an example of how this is done. + +## Documentation + +See [TypeDoc generated documentation](https://zelonewolf.github.io/openstreetmap-americana/shield-docs/index.html) for detailed API information. diff --git a/shieldlib/package.json b/shieldlib/package.json new file mode 100644 index 000000000..f92282f28 --- /dev/null +++ b/shieldlib/package.json @@ -0,0 +1,64 @@ +{ + "name": "@americana/maplibre-shield-generator", + "description": "Generate highway shields for maplibre-gl-js maps", + "version": "0.0.6", + "author": "OpenStreetMap Americana Contributors", + "type": "module", + "keywords": [ + "americana", + "highway", + "shields", + "shield", + "generator", + "maplibre", + "maplibre-gl", + "maplibre-gl-js" + ], + "license": "CC0-1.0", + "main": "dist/index.js", + "source": "src/index.ts", + "devDependencies": { + "@types/color-rgba": "^2.1.0", + "canvas": "^2.11.2", + "esbuild": "^0.17.10", + "npm-run-all": "^4.1.5", + "prettier": "^2.8.4", + "shx": "^0.3.4", + "tsx": "^4.6.2", + "typedoc": "^0.24.8", + "typescript": "^4.9.5" + }, + "scripts": { + "code_format": "run-s code_format:prettier", + "code_format:prettier": "prettier --write --list-different .", + "clean": "shx rm -rf dist docs", + "docs": "typedoc src/index.ts", + "test": "mocha", + "build:code": "node scripts/build.js", + "build": "run-s clean build:code docs", + "preversion": "npm version --no-git-tag-version --preid alpha", + "publish-alpha": "npm publish --access=public --tag alpha" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ZeLonewolf/openstreetmap-americana.git" + }, + "bugs": { + "url": "https://github.com/ZeLonewolf/openstreetmap-americana/issues" + }, + "homepage": "https://github.com/ZeLonewolf/openstreetmap-americana#readme", + "dependencies": { + "@types/node": "^20.8.4", + "color-rgba": "^2.4.0", + "maplibre-gl": "^2.4.0", + "mocha": "^10.2.0" + }, + "directories": { + "test": "test" + }, + "files": [ + "dist/", + "docs/", + "README.md" + ] +} diff --git a/shieldlib/scripts/build.js b/shieldlib/scripts/build.js new file mode 100644 index 000000000..e9bb97ce2 --- /dev/null +++ b/shieldlib/scripts/build.js @@ -0,0 +1,37 @@ +import { mkdir } from "node:fs/promises"; +import { pathToFileURL } from "node:url"; + +import esbuild from "esbuild"; + +const buildWith = async (key, buildOptions) => { + await mkdir("dist", { recursive: true }); + + const options = { + entryPoints: ["src/index.ts"], + format: "esm", + bundle: true, + minify: true, + sourcemap: true, + outdir: "dist", + logLevel: "info", + ...buildOptions, + define: { + ...buildOptions?.define, + }, + }; + return ( + esbuild[key](options) + // esbuild will pretty-print its own error messages; + // suppress node.js from printing the exception. + .catch(() => process.exit(1)) + ); +}; + +export const buildContext = (buildOptions = {}) => + buildWith("context", buildOptions); + +export const build = (buildOptions = {}) => buildWith("build", buildOptions); + +const mainModule = pathToFileURL(process.argv[1]).toString(); + +await build({}); diff --git a/shieldlib/src/custom_shields.ts b/shieldlib/src/custom_shields.ts new file mode 100644 index 000000000..dcf7da765 --- /dev/null +++ b/shieldlib/src/custom_shields.ts @@ -0,0 +1,82 @@ +"use strict"; + +import * as ShieldDraw from "./shield_canvas_draw"; +import { ShieldRenderingContext } from "./shield_renderer"; +import { ShapeBlankParams } from "./types"; + +// Special case for Allegheny, PA Belt System +export function paBelt( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams +): number { + ShieldDraw.roundedRectangle(r, ctx, { + fillColor: "white", + strokeColor: "black", + outlineWidth: 1, + radius: 2, + rectWidth: 20, + }); + + let fillColor = params.fillColor; + + let lineWidth = r.px(0.5); + let diameter = r.shieldSize() / 3 - lineWidth; + ctx.beginPath(); + ctx.arc( + r.shieldSize() / 2, + r.shieldSize() / 2, + diameter, + 0, + 2 * Math.PI, + false + ); + + ctx.fillStyle = fillColor; + ctx.strokeStyle = params.strokeColor; + ctx.fill(); + + ctx.lineWidth = lineWidth; + ctx.stroke(); + return 20; +} + +// Special case for Branson color-coded routes +export function bransonRoute( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams +): number { + ShieldDraw.roundedRectangle(r, ctx, { + fillColor: "#006747", + strokeColor: "white", + outlineWidth: 1, + radius: 2, + rectWidth: 20, + }); + + let fillColor = params.fillColor; + + let lineWidth = r.px(0.5); + let x = 0.15 * r.shieldSize() + lineWidth; + let width = 0.7 * r.shieldSize() - 2 * lineWidth; + + let y = 0.4 * r.shieldSize() + lineWidth; + let height = 0.45 * r.shieldSize() - 2 * lineWidth; + + ctx.beginPath(); + ctx.rect(x, y, width, height); + + ctx.fillStyle = fillColor; + ctx.strokeStyle = params.strokeColor; + ctx.fill(); + + ctx.lineWidth = lineWidth; + ctx.stroke(); + return 20; +} + +export function loadCustomShields() { + ShieldDraw.registerDrawFunction("branson", bransonRoute, 20); + ShieldDraw.registerDrawFunction("paBelt", paBelt, 20); +} diff --git a/shieldlib/src/document_graphics.d.ts b/shieldlib/src/document_graphics.d.ts new file mode 100644 index 000000000..6402f6026 --- /dev/null +++ b/shieldlib/src/document_graphics.d.ts @@ -0,0 +1,11 @@ +import { Bounds, GraphicsFactory } from "./types"; +/** + * Get the Maplibre pixel reation being used in this browser. This determines + * which sprite sheet is used and whether or not to double dimensions on graphics draws. + * @returns Returns either 1 or 2 for "1x" or "2x+" displays. + */ +export declare function getDOMPixelRatio(): 1 | 2; +export declare class DOMGraphicsFactory implements GraphicsFactory { + createGraphics(bounds: Bounds): CanvasRenderingContext2D; + pixelRatio(): number; +} diff --git a/shieldlib/src/document_graphics.ts b/shieldlib/src/document_graphics.ts new file mode 100644 index 000000000..24335decc --- /dev/null +++ b/shieldlib/src/document_graphics.ts @@ -0,0 +1,28 @@ +import { Bounds, GraphicsFactory } from "./types"; + +/** + * Get the Maplibre pixel reation being used in this browser. This determines + * which sprite sheet is used and whether or not to double dimensions on graphics draws. + * @returns Returns either 1 or 2 for "1x" or "2x+" displays. + */ +export function getDOMPixelRatio(): 1 | 2 { + return (typeof window !== "undefined" && window.devicePixelRatio) > 1 ? 2 : 1; +} + +export class DOMGraphicsFactory implements GraphicsFactory { + createGraphics(bounds: Bounds): CanvasRenderingContext2D { + var ctx = document.createElement("canvas").getContext("2d"); + var ctx = document + .createElement("canvas") + .getContext("2d", { willReadFrequently: true }); + ctx.imageSmoothingQuality = "high"; + ctx.textAlign = "center"; + ctx.textBaseline = "top"; + ctx.canvas.width = bounds.width; + ctx.canvas.height = bounds.height; + return ctx; + } + pixelRatio(): number { + return getDOMPixelRatio(); + } +} diff --git a/shieldlib/src/headless_graphics.ts b/shieldlib/src/headless_graphics.ts new file mode 100644 index 000000000..08c47ace4 --- /dev/null +++ b/shieldlib/src/headless_graphics.ts @@ -0,0 +1,19 @@ +import { Bounds, GraphicsFactory } from "./types"; +import { createCanvas } from "canvas"; + +export class HeadlessGraphicsFactory implements GraphicsFactory { + private _type: "pdf" | "svg"; + constructor(type: "pdf" | "svg") { + this._type = type; + } + createGraphics(bounds: Bounds): CanvasRenderingContext2D { + let canvas = createCanvas(bounds.width, bounds.height, this._type); + let ctx = canvas.getContext("2d"); + ctx.textAlign = "center"; + ctx.textBaseline = "top"; + return ctx as any as CanvasRenderingContext2D; + } + pixelRatio(): number { + return 1; + } +} diff --git a/shieldlib/src/index.d.ts b/shieldlib/src/index.d.ts new file mode 100644 index 000000000..46116e4fa --- /dev/null +++ b/shieldlib/src/index.d.ts @@ -0,0 +1,10 @@ +export * from "./types"; +export { transposeImageData } from "./screen_gfx"; +export { + URLShieldRenderer, + ShieldRenderer, + InMemorySpriteRepository, + AbstractShieldRenderer, +} from "./shield_renderer"; +export { getDOMPixelRatio } from "./document_graphics"; +export * from "./shield_helper"; diff --git a/shieldlib/src/index.ts b/shieldlib/src/index.ts new file mode 100644 index 000000000..97b3a5991 --- /dev/null +++ b/shieldlib/src/index.ts @@ -0,0 +1,13 @@ +export * from "./types"; + +export { transposeImageData } from "./screen_gfx"; + +export { + URLShieldRenderer, + ShieldRenderer, + InMemorySpriteRepository, + AbstractShieldRenderer, +} from "./shield_renderer"; + +export { getDOMPixelRatio } from "./document_graphics"; +export * from "./shield_helper"; diff --git a/shieldlib/src/screen_gfx.d.ts b/shieldlib/src/screen_gfx.d.ts new file mode 100644 index 000000000..404299cbb --- /dev/null +++ b/shieldlib/src/screen_gfx.d.ts @@ -0,0 +1,30 @@ +import { StyleImage } from "maplibre-gl"; +export declare const shieldFont: (size: string, fontFamily: string) => string; +export declare const fontSizeThreshold = 12; +export declare function blendedColorVal( + sourceVal: any, + lightenVal: any, + darkenVal: any +): number; +export declare function copyPixel( + source: any, + dest: any, + sourceOffset: any, + destOffset: any +): void; +export declare function copyAndRecolorPixel( + source: any, + dest: any, + sourceOffset: any, + destOffset: any, + colorLighten: any, + colorDarken: any +): void; +export declare function transposeImageData( + destination: CanvasRenderingContext2D, + source: StyleImage, + yOffset: number, + verticalReflect: boolean, + colorLighten: string, + colorDarken: string +): void; diff --git a/shieldlib/src/screen_gfx.ts b/shieldlib/src/screen_gfx.ts new file mode 100644 index 000000000..3be40e4be --- /dev/null +++ b/shieldlib/src/screen_gfx.ts @@ -0,0 +1,106 @@ +import { StyleImage } from "maplibre-gl"; + +import rgba from "color-rgba"; + +const defaultFontFamily = '"sans-serif-condensed", "Arial Narrow", sans-serif'; +export const shieldFont = (size: number, fontFamily: string) => + `condensed 500 ${size}px ${fontFamily || defaultFontFamily}`; + +//If a computed shield font size is below this value, choose a wider shield if possible +export const fontSizeThreshold = 11.8; + +// Replaces `sourceVal` with a blend of `lightenVal` and `darkenVal` proportional to the brightness; +// i.e. white becomes `darkenVal`, black becomes `lightenVal`, and anit-aliased pixels remain anit-aliased +export function blendedColorVal(sourceVal, lightenVal, darkenVal) { + return ( + 255 - + (sourceVal / 255) * (255 - darkenVal) - + (1 - sourceVal / 255) * (255 - lightenVal) + ); +} + +export function copyPixel(source, dest, sourceOffset, destOffset) { + //Red + dest[destOffset] = source[sourceOffset]; + //Green + dest[destOffset + 1] = source[sourceOffset + 1]; + //Blue + dest[destOffset + 2] = source[sourceOffset + 2]; + //Alpha + dest[destOffset + 3] = source[sourceOffset + 3]; +} + +export function copyAndRecolorPixel( + source, + dest, + sourceOffset, + destOffset, + colorLighten, + colorDarken +) { + //Red + dest[destOffset] = blendedColorVal( + source[sourceOffset], + colorLighten[0], + colorDarken[0] + ); + //Green + dest[destOffset + 1] = blendedColorVal( + source[sourceOffset + 1], + colorLighten[1], + colorDarken[1] + ); + //Blue + dest[destOffset + 2] = blendedColorVal( + source[sourceOffset + 2], + colorLighten[2], + colorDarken[2] + ); + //Alpha + dest[destOffset + 3] = source[sourceOffset + 3]; +} + +export function transposeImageData( + destination: CanvasRenderingContext2D, + source: StyleImage, + yOffset: number, + verticalReflect: boolean, + colorLighten: string, + colorDarken: string +): void { + let imgData = destination.createImageData( + source.data.width, + source.data.height + ); + + let pixelCopyFn = + colorLighten || colorDarken ? copyAndRecolorPixel : copyPixel; + + let lighten = colorLighten ? rgba(colorLighten) : rgba("#000"); + let darken = colorDarken ? rgba(colorDarken) : rgba("#fff"); + + if (!verticalReflect) { + for (let i = 0; i < source.data.data.length; i += 4) { + pixelCopyFn(source.data.data, imgData.data, i, i, lighten, darken); + } + } else { + //4 bytes/px, copy in reverse vertical order. + for (let y = 0; y < source.data.height; y++) { + for (let x = 0; x < source.data.width; x++) { + let destRow = source.data.height - y - 1; + let destIdx = (destRow * source.data.width + x) * 4; + let srcIdx = (y * source.data.width + x) * 4; + pixelCopyFn( + source.data.data, + imgData.data, + srcIdx, + destIdx, + lighten, + darken + ); + } + } + } + + destination.putImageData(imgData, 0, yOffset); +} diff --git a/shieldlib/src/shield.d.ts b/shieldlib/src/shield.d.ts new file mode 100644 index 000000000..38e055424 --- /dev/null +++ b/shieldlib/src/shield.d.ts @@ -0,0 +1,22 @@ +import { RouteDefinition } from "./types"; + +import { ShieldRenderingContext } from "./shield_renderer"; + +export function generateShieldCtx( + renderContext: ShieldRenderingContext, + routeDef: RouteDefinition +): CanvasRenderingContext2D; + +export function storeNoShield( + renderingContext: ShieldRenderingContext, + id: string +): void; + +export function missingIconLoader( + renderContext: ShieldRenderingContext, + routeDef: RouteDefinition, + spriteID: string, + update?: boolean +): void; + +export function romanizeRef(ref: string): string; diff --git a/shieldlib/src/shield.ts b/shieldlib/src/shield.ts new file mode 100644 index 000000000..b96749a57 --- /dev/null +++ b/shieldlib/src/shield.ts @@ -0,0 +1,437 @@ +"use strict"; + +import * as ShieldText from "./shield_text"; +import * as ShieldDraw from "./shield_canvas_draw"; +import * as Gfx from "./screen_gfx"; +import { drawBanners, drawBannerHalos, getBannerCount } from "./shield_banner"; +import { ShieldRenderingContext } from "./shield_renderer"; +import { + Dimension, + RouteDefinition, + ShieldDefinition, + ShieldDefinitions, +} from "./types"; +import { TextPlacement } from "./shield_text"; +import { StyleImage } from "maplibre-gl"; + +const narrowCharacters = /[1IJijl .-]/g; + +function compoundShieldSize( + r: ShieldRenderingContext, + dimension: Dimension, + bannerCount: number +): Dimension { + return { + width: dimension.width, + height: + dimension.height + + bannerCount * r.px(r.options.bannerHeight + r.options.bannerPadding), + }; +} + +export function isValidRef(ref: string): boolean { + return ref !== null && ref.length !== 0 && ref.length <= 6; +} + +/** + * Retrieve the shield blank that goes with a particular route. If there are + * multiple shields for a route (different widths), it picks the best shield. + * + * @param {*} r - render context + * @param {*} shieldDef - shield definition for this route + * @param {*} routeDef - route tagging from OSM + * @returns shield blank or null if no shield exists + */ +function getRasterShieldBlank( + r: ShieldRenderingContext, + shieldDef: ShieldDefinition, + routeDef: RouteDefinition +): StyleImage { + let shieldArtwork = null; + let textPlacement: TextPlacement; + let bannerCount: number = 0; + let bounds: Dimension; + + if (Array.isArray(shieldDef.spriteBlank)) { + // Certain narrow characters count as two-thirds of a character + let narrowCharacterCount = (routeDef.ref.match(narrowCharacters) ?? []) + .length; + let refLength = Math.ceil(routeDef.ref.length - narrowCharacterCount / 3); + + // Choose icon based on optimal character length at end of filename + let finalIndex = shieldDef.spriteBlank.length - 1; + let optimalCharacters = shieldDef.spriteBlank.map((blank) => + parseInt(blank.split("_").reverse()[0]) + ); + let spriteIndex = + refLength > optimalCharacters[finalIndex] + ? finalIndex + : Math.max(0, optimalCharacters.indexOf(refLength)); + + shieldArtwork = r.spriteRepo.getSprite(shieldDef.spriteBlank[spriteIndex]); + } else { + shieldArtwork = r.spriteRepo.getSprite(shieldDef.spriteBlank); + } + + return shieldArtwork; +} + +function textColor(shieldDef: ShieldDefinition): string { + if (shieldDef != null && typeof shieldDef.textColor != "undefined") { + return shieldDef.textColor; + } + return "black"; +} + +function getDrawFunc( + shieldDef: ShieldDefinition +): ( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + ref: string +) => void { + if (typeof shieldDef.shapeBlank != "undefined") { + return ( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + ref: string + ) => + ShieldDraw.draw( + r, + shieldDef.shapeBlank.drawFunc, + ctx, + shieldDef.shapeBlank.params, + ref + ); + } + console.warn(`Draw function not defined in:\n${shieldDef}`); + return ( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + ref: string + ) => {}; +} + +function getDrawHeight( + r: ShieldRenderingContext, + shieldDef: ShieldDefinition +): number { + if (typeof shieldDef.shapeBlank != "undefined") { + return ShieldDraw.shapeHeight(r, shieldDef.shapeBlank.drawFunc); + } + return r.shieldSize(); +} + +function drawShieldText( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + shieldDef: ShieldDefinition, + routeDef: RouteDefinition, + shieldBounds: Dimension +): CanvasRenderingContext2D { + if (shieldDef.notext) { + //If the shield definition says not to draw a ref, ignore ref + return ctx; + } + + //The ref is valid and we're supposed to draw it + var textLayout = ShieldText.layoutShieldTextFromDef( + r, + routeDef.ref, + shieldDef, + shieldBounds + ); + + if (typeof r.debugOptions?.shieldTextHaloColor !== "undefined") { + ctx.strokeStyle = r.debugOptions.shieldTextHaloColor; + ShieldText.drawShieldHaloText(r, ctx, routeDef.ref, textLayout); + } else if (shieldDef.textHaloColor) { + ctx.strokeStyle = shieldDef.textHaloColor; + ShieldText.drawShieldHaloText(r, ctx, routeDef.ref, textLayout); + } + + ctx.fillStyle = textColor(shieldDef); + ShieldText.renderShieldText(r, ctx, routeDef.ref, textLayout); + + if (r.debugOptions?.shieldTextBboxColor) { + ctx.strokeStyle = r.debugOptions.shieldTextBboxColor; + ctx.lineWidth = r.px(1); + ctx.strokeRect( + r.px(shieldDef.padding.left - 0.5), + r.px(shieldDef.padding.top - 0.5), + shieldBounds.width - + r.px(shieldDef.padding.left + shieldDef.padding.right - 1), + shieldBounds.height - + r.px(shieldDef.padding.top + shieldDef.padding.bottom - 1) + ); + } + + return ctx; +} + +export function missingIconLoader( + r: ShieldRenderingContext, + routeDef: RouteDefinition, + spriteID: string, + update: boolean +): void { + let ctx = generateShieldCtx(r, routeDef); + if (ctx == null) { + // Want to return null here, but that gives a corrupted display. See #243 + console.warn("Didn't produce a shield for", JSON.stringify(routeDef)); + ctx = r.gfxFactory.createGraphics({ width: 1, height: 1 }); + } + storeSprite(r, spriteID, ctx, update); +} + +function storeSprite( + r: ShieldRenderingContext, + id: string, + ctx: CanvasRenderingContext2D, + update: boolean +): void { + const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); + r.spriteRepo.putSprite( + id, + { + width: ctx.canvas.width, + height: ctx.canvas.height, + data: imgData.data, + }, + { pixelRatio: r.px(1) }, + update + ); +} + +export function storeNoShield(r: ShieldRenderingContext, id: string): void { + storeSprite(r, id, r.emptySprite(), false); +} + +function refForDefs(routeDef: RouteDefinition, shieldDef: ShieldDefinition) { + // Handle special case for manually-applied abbreviations + if (shieldDef.ref) { + return shieldDef.ref; + } + if ( + shieldDef.refsByName && + routeDef.name && + shieldDef.refsByName[routeDef.name] + ) { + return shieldDef.refsByName[routeDef.name]; + } + return routeDef.ref; +} + +function getShieldDef( + shields: ShieldDefinitions, + routeDef: RouteDefinition +): ShieldDefinition { + if (!shields) { + //This occurs if the ShieldJSON is loaded from the network and hasn't loaded yet. + return null; + } + + let shieldDef: ShieldDefinition = shields[routeDef.network]; + + if (routeDef == null) { + return null; + } + + if (shieldDef == null) { + // Default to plain black text with halo and no background shield + console.debug("Generic shield for", JSON.stringify(routeDef)); + + return isValidRef(routeDef.ref) ? shields["default"] : null; + } + + var ref = refForDefs(routeDef, shieldDef); + + if (shieldDef.overrideByRef) { + shieldDef = { + ...shieldDef, + ...shieldDef.overrideByRef[ref], + }; + } + + if (shieldDef.overrideByName) { + shieldDef = { + ...shieldDef, + ...shieldDef.overrideByName[routeDef.name || ""], + }; + } + + //Special case where there's a defined fallback shield when no ref is tagged + //Example: PA Turnpike + if (!isValidRef(ref) && "noref" in shieldDef) { + shieldDef = shieldDef.noref; + // noref implies notext + shieldDef.notext = true; + } + + //Determine whether a route without a ref gets drawn + if ( + !isValidRef(ref) && + !shieldDef.notext && + !shieldDef.ref && + !(shieldDef.refsByName && routeDef.name) + ) { + return null; + } + + return shieldDef; +} + +export const arabizeRef = (ref) => + ref.replaceAll(/[0-9]/g, (m) => String.fromCharCode(0x0660 + parseInt(m))); + +/** + * Reformats an alphanumeric ref as Roman numerals, preserving any alphabetic + * suffix. + */ +export function romanizeRef(ref: string): string { + let number = parseInt(ref, 10); + if (isNaN(number)) { + return ref; + } + + let roman = + "M".repeat(number / 1000) + + "D".repeat((number % 1000) / 500) + + "C".repeat((number % 500) / 100) + + "L".repeat((number % 100) / 50) + + "X".repeat((number % 50) / 10) + + "V".repeat((number % 10) / 5) + + "I".repeat(number % 5); + roman = roman + .replace("DCCCC", "CM") + .replace("CCCC", "CD") + .replace("LXXXX", "XC") + .replace("XXXX", "XL") + .replace("VIIII", "IX") + .replace("IIII", "IV"); + return roman + ref.slice(number.toString().length); +} + +function getDrawnShieldBounds( + r: ShieldRenderingContext, + shieldDef: ShieldDefinition, + ref: string +): Dimension { + let width = Math.max( + r.shieldSize(), + ShieldDraw.computeWidth( + r, + shieldDef.shapeBlank.params, + ref, + shieldDef.shapeBlank.drawFunc + ) + ); + let height = ShieldDraw.shapeHeight(r, shieldDef.shapeBlank.drawFunc); + + return { width, height }; +} + +function bannerAreaHeight( + r: ShieldRenderingContext, + bannerCount: number +): number { + if (bannerCount === 0) { + return 0; + } + return ( + bannerCount * r.px(r.options.bannerHeight) + + //No padding after last banner + (bannerCount - 1) * r.px(r.options.bannerPadding) + ); +} + +export function generateShieldCtx( + r: ShieldRenderingContext, + routeDef: RouteDefinition +): CanvasRenderingContext2D { + let shieldDef: ShieldDefinition = getShieldDef(r.shieldDef, routeDef); + + if (shieldDef == null) { + return null; + } + + routeDef.ref = refForDefs(routeDef, shieldDef); + + //Determine overall shield+banner dimensions + let bannerCount = getBannerCount(shieldDef); + + let sourceSprite = getRasterShieldBlank(r, shieldDef, routeDef); + + let width = r.shieldSize(); + let height = r.shieldSize(); + + let shieldBounds = null; + + if (sourceSprite == null) { + if (typeof shieldDef.shapeBlank != "undefined") { + let bounds = getDrawnShieldBounds(r, shieldDef, routeDef.ref); + width = bounds.width; + height = bounds.height; + } + shieldBounds = { + width: width, + height: getDrawHeight(r, shieldDef), + }; + } else { + width = sourceSprite.data.width; + height = sourceSprite.data.height; + shieldBounds = { width, height }; + } + + let bannerHeight = bannerAreaHeight(r, bannerCount); + height += bannerHeight; + + //Generate empty canvas sized to the graphic + let ctx = r.gfxFactory.createGraphics({ width, height }); + + // Convert numbering systems. Normally alternative numbering systems should be + // tagged directly in ref=*, but some shields use different numbering systems + // for aesthetic reasons only. + if (routeDef.ref) { + switch (shieldDef.numberingSystem) { + case "arab": + routeDef.ref = arabizeRef(routeDef.ref); + break; + case "roman": + routeDef.ref = romanizeRef(routeDef.ref); + break; + } + } + + // Add the halo around modifier plaque text + drawBannerHalos(r, ctx, shieldDef); + + //Shift canvas to draw shield below banner + ctx.save(); + ctx.translate(0, bannerHeight); + + if (sourceSprite == null) { + let drawFunc = getDrawFunc(shieldDef); + drawFunc(r, ctx, routeDef.ref); + } else { + //This is a raw copy, so the yOffset (bannerHeight) is needed + Gfx.transposeImageData( + ctx, + sourceSprite, + bannerHeight, + shieldDef.verticalReflect, + shieldDef.colorLighten, + shieldDef.colorDarken + ); + } + + // Draw the shield text + drawShieldText(r, ctx, shieldDef, routeDef, shieldBounds); + + ctx.restore(); + + // Add modifier plaque text + drawBanners(r, ctx, shieldDef); + + return ctx; +} diff --git a/shieldlib/src/shield_banner.ts b/shieldlib/src/shield_banner.ts new file mode 100644 index 000000000..deb86599d --- /dev/null +++ b/shieldlib/src/shield_banner.ts @@ -0,0 +1,179 @@ +import { shieldFont } from "./screen_gfx"; +import { ShieldRenderingContext } from "./shield_renderer"; +import { TextPlacement, layoutShieldText } from "./shield_text"; +import { Dimension, ShieldDefinition, TextLayout } from "./types"; + +let bannerLayout: TextLayout = { + constraintFunc: "rect", +}; + +/** + * Add modifier plaque text + * + * @param r - Shield rendering context + * @param ctx - Canvas drawing context + * @param shieldDef - Shield definition + */ +export function drawBanners( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + shieldDef: ShieldDefinition +) { + if (shieldDef.bannerTextColor) { + ctx.fillStyle = shieldDef.bannerTextColor; + } else { + ctx.fillStyle = r.options.bannerTextColor; + } + drawBannerPart(r, ctx, shieldDef, drawBannerText); +} + +/** + * Add the halo around modifier plaque text + * + * @param r - Shield rendering context + * @param ctx - Canvas drawing context + * @param shieldDef - Shield definition + */ +export function drawBannerHalos( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + shieldDef: ShieldDefinition +) { + if (shieldDef.bannerTextHaloColor) { + ctx.strokeStyle = ctx.shadowColor = shieldDef.bannerTextHaloColor; + } else { + ctx.strokeStyle = ctx.shadowColor = r.options.bannerTextHaloColor; + } + drawBannerPart(r, ctx, shieldDef, drawBannerHaloText); +} + +type BannerDrawComponentFunction = ( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + text: string, + bannerIndex: number +) => void; + +function drawBannerPart( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + shieldDef: ShieldDefinition, + drawFunc: BannerDrawComponentFunction +): void { + if (shieldDef == null || typeof shieldDef.banners == "undefined") { + return; //Unadorned shield + } + + for (var i = 0; i < shieldDef.banners.length; i++) { + drawFunc(r, ctx, shieldDef.banners[i], i); + } +} + +/** + * Get the number of banner placards associated with this shield + * + * @param shield - Shield definition + * @returns the number of banner placards that need to be drawn + */ +export function getBannerCount(shield: ShieldDefinition): number { + if (shield == null || typeof shield.banners == "undefined") { + return 0; //Unadorned shield + } + return shield.banners.length; +} + +/** + * Draw text on a modifier plate above a shield + * + * @param {*} r - rendering context + * @param {*} ctx - graphics context to draw to + * @param {*} text - text to draw + * @param {*} bannerIndex - plate position to draw, 0=top, incrementing + */ +export function drawBannerText( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + text: string, + bannerIndex: number +): void { + drawBannerTextComponent(r, ctx, text, bannerIndex, true); +} + +/** + * Draw drop shadow for text on a modifier plate above a shield + * + * @param {*} r - rendering context + * @param {*} ctx - graphics context to draw to + * @param {*} text - text to draw + * @param {*} bannerIndex - plate position to draw, 0=top, incrementing + */ +export function drawBannerHaloText( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + text: string, + bannerIndex: number +): void { + drawBannerTextComponent(r, ctx, text, bannerIndex, false); +} + +/** + * Banners are composed of two components: text on top, and a shadow beneath. + * + * @param {*} r - rendering context + * @param {*} ctx - graphics context to draw to + * @param {*} text - text to draw + * @param {*} bannerIndex - plate position to draw, 0=top, incrementing + * @param {*} textComponent - if true, draw the text. If false, draw the halo + */ +function drawBannerTextComponent( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + text: string, + bannerIndex: number, + textComponent: boolean +): void { + const bannerPadding = { + top: 0, + bottom: 0, + left: 0, + right: 0, + }; + + let bannerBounds: Dimension = { + width: ctx.canvas.width, + height: r.px(r.options.bannerHeight - r.options.bannerPadding), + }; + + let textLayout: TextPlacement = layoutShieldText( + r, + text, + bannerPadding, + bannerBounds, + bannerLayout + ); + + ctx.font = shieldFont(textLayout.fontPx, r.options.shieldFont); + ctx.textBaseline = "top"; + ctx.textAlign = "center"; + + if (textComponent) { + ctx.fillText( + text, + textLayout.xBaseline, + textLayout.yBaseline + + bannerIndex * r.px(r.options.bannerHeight + r.options.bannerPadding) + ); + } else { + ctx.shadowBlur = 0; + ctx.lineWidth = r.px(2); + ctx.strokeText( + text, + textLayout.xBaseline, + textLayout.yBaseline + + bannerIndex * r.px(r.options.bannerHeight + r.options.bannerPadding) + ); + + ctx.shadowColor = null; + ctx.shadowBlur = null; + } +} diff --git a/src/js/shield_canvas_draw.js b/shieldlib/src/shield_canvas_draw.ts similarity index 67% rename from src/js/shield_canvas_draw.js rename to shieldlib/src/shield_canvas_draw.ts index 994f71e91..a4370347e 100644 --- a/src/js/shield_canvas_draw.js +++ b/shieldlib/src/shield_canvas_draw.ts @@ -4,74 +4,86 @@ * Shield blanks which are drawn rather built from raster shield blanks */ -import * as Gfx from "./screen_gfx.js"; -import * as ShieldText from "./shield_text.js"; - -export const PXR = Gfx.getPixelRatio(); - -// Canvas size in pixels. Length of smallest dimension (typically height) -export const CS = 20 * PXR; - -const minGenericShieldWidth = 20 * PXR; -const maxGenericShieldWidth = 34 * PXR; -const genericShieldFontSize = 18 * PXR; - -export function computeWidth(params, ref, shape) { +import * as ShieldText from "./shield_text"; +import { loadCustomShields } from "./custom_shields"; +import { ShapeDrawFunction, ShieldRenderingContext } from "./shield_renderer"; +import { ShapeBlankParams } from "./types"; + +const minGenericShieldWidth = 20; +const maxGenericShieldWidth = 34; +const genericShieldFontSize = 18; + +export function computeWidth( + r: ShieldRenderingContext, + params: ShapeBlankParams, + ref: string, + shape?: string +) { if (fixedWidthDefinitions[shape] !== undefined) { - return fixedWidthDefinitions[shape]; + return r.px(fixedWidthDefinitions[shape]); } let rectWidth = params.rectWidth == undefined ? null : params.rectWidth; - let angle = params.angle == undefined ? 0 : params.angle; - let tangent = Math.tan(angle); + let sideAngle = params.sideAngle == undefined ? 0 : params.sideAngle; + let tangent = Math.tan(sideAngle); if (rectWidth == null) { let shieldWidth = - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; - let minWidth = minGenericShieldWidth; + ShieldText.calculateTextWidth(r, ref, r.px(genericShieldFontSize)) + + r.px(2); + let minWidth = r.px(minGenericShieldWidth); //Shape-specific width adjustments switch (shape) { case "pentagon": - let offset = params.offset == undefined ? 0 : params.offset; - shieldWidth += ((CS - offset * PXR) * tangent) / 2; + let yOffset = params.yOffset == undefined ? 0 : params.yOffset; + shieldWidth += ((r.shieldSize() - r.px(yOffset)) * tangent) / 2; break; case "trapezoid": - shieldWidth += (CS * tangent) / 2; + shieldWidth += (r.shieldSize() * tangent) / 2; break; case "triangle": - minWidth += 2 * PXR; + minWidth += r.px(2); break; case "diamond": case "hexagonHorizontal": - minWidth += 4 * PXR; + minWidth += r.px(4); break; } - return Math.max(minWidth, Math.min(maxGenericShieldWidth, shieldWidth)); + return Math.max( + minWidth, + Math.min(r.px(maxGenericShieldWidth), shieldWidth) + ); } else { - return rectWidth * PXR; + return r.px(rectWidth); } } -function ellipse(ctx, params, ref) { +function ellipse( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; - let width = computeWidth(params, ref); + let width = computeWidth(r, params, ref); - let lineWidth = PXR; + let lineWidth = r.px(1); let radiusX = width / 2 - lineWidth; - let radiusY = CS / 2 - lineWidth; + let radiusY = r.shieldSize() / 2 - lineWidth; ctx.beginPath(); ctx.ellipse( ctx.canvas.width / 2, - CS / 2, + r.shieldSize() / 2, radiusX, radiusY, 0, + 0, 2 * Math.PI, false ); @@ -85,27 +97,36 @@ function ellipse(ctx, params, ref) { return width; } -export function blank(ref) { - var shieldWidth = - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; - var width = Math.max( - minGenericShieldWidth, - Math.min(maxGenericShieldWidth, shieldWidth) - ); - return Gfx.getGfxContext({ width: width, height: CS }); +export function pill( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref?: string +): number { + /* + shieldSize() has to be divided by r.px(1) to get down to 1x resolution. + Then we divide in half again to get a 50% height corner radius, hence px(2). + */ + params.radius = r.shieldSize() / r.px(2); + return roundedRectangle(r, ctx, params, ref); } -export function roundedRectangle(ctx, params, ref) { +export function roundedRectangle( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref?: string +): number { let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; - let width = computeWidth(params, ref); + let width = computeWidth(r, params, ref); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; + let drawRadius = r.px(radius); let x0 = lineWidth; let x1 = lineWidth + drawRadius; @@ -114,8 +135,8 @@ export function roundedRectangle(ctx, params, ref) { let y0 = lineWidth; let y1 = lineWidth + drawRadius; - let y2 = CS - lineWidth - drawRadius; - let y3 = CS - lineWidth; + let y2 = r.shieldSize() - lineWidth - drawRadius; + let y3 = r.shieldSize() - lineWidth; ctx.beginPath(); ctx.moveTo(x2, y0); @@ -137,25 +158,30 @@ export function roundedRectangle(ctx, params, ref) { return width; } -function escutcheon(ctx, params, ref) { - let offset = params.offset == undefined ? 0 : params.offset; +function escutcheon( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { + let yOffset = params.yOffset == undefined ? 0 : params.yOffset; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; - let width = computeWidth(params, ref); + let width = computeWidth(r, params, ref); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; - let drawOffset = offset * PXR; + let drawRadius = r.px(radius); + let drawOffset = r.px(yOffset); let x0 = lineWidth; let x5 = width - lineWidth; let y0 = lineWidth; - let y5 = CS - lineWidth; + let y5 = r.shieldSize() - lineWidth; let x1 = x0 + drawRadius; let x3 = (x0 + x5) / 2; @@ -189,7 +215,12 @@ function escutcheon(ctx, params, ref) { return width; } -function fishhead(ctx, params, ref) { +function fishhead( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { let pointUp = params.pointUp == undefined ? false : params.pointUp; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; @@ -197,27 +228,27 @@ function fishhead(ctx, params, ref) { let angleSign = pointUp ? -1 : 1; - let width = computeWidth(params, ref); + let width = computeWidth(r, params, ref); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; let x0 = lineWidth; let x8 = width - lineWidth; - let y0 = pointUp ? CS - lineWidth : lineWidth; - let y6 = pointUp ? lineWidth : CS - lineWidth; + let y0 = pointUp ? r.shieldSize() - lineWidth : lineWidth; + let y6 = pointUp ? lineWidth : r.shieldSize() - lineWidth; - let x1 = x0 + PXR; - let x2 = x0 + 2.5 * PXR; + let x1 = x0 + r.px(1); + let x2 = x0 + r.px(2.5); let x4 = (x0 + x8) / 2; - let x6 = x8 - 2.5 * PXR; - let x7 = x8 - PXR; - let y1 = y0 + angleSign * 2 * PXR; - let y2 = y0 + angleSign * 4.5 * PXR; - let y3 = y0 + angleSign * 7 * PXR; - let y4 = y6 - angleSign * 6 * PXR; - let y5 = y6 - angleSign * PXR; + let x6 = x8 - r.px(2.5); + let x7 = x8 - r.px(1); + let y1 = y0 + angleSign * r.px(2); + let y2 = y0 + angleSign * r.px(4.5); + let y3 = y0 + angleSign * r.px(7); + let y4 = y6 - angleSign * r.px(6); + let y5 = y6 - angleSign * r.px(1); let x3 = (x0 + x4) / 2; let x5 = (x4 + x8) / 2; @@ -239,9 +270,16 @@ function fishhead(ctx, params, ref) { ctx.strokeStyle = outline; ctx.stroke(); } + + return width; } -function triangle(ctx, params, ref) { +function triangle( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { let pointUp = params.pointUp == undefined ? false : params.pointUp; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; @@ -250,16 +288,16 @@ function triangle(ctx, params, ref) { let angleSign = pointUp ? -1 : 1; - let width = computeWidth(params, ref, "triangle"); + let width = computeWidth(r, params, ref, "triangle"); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; + let drawRadius = r.px(radius); let x0 = lineWidth; let x8 = width - lineWidth; - let y0 = pointUp ? CS - lineWidth : lineWidth; - let y5 = pointUp ? lineWidth : CS - lineWidth; + let y0 = pointUp ? r.shieldSize() - lineWidth : lineWidth; + let y5 = pointUp ? lineWidth : r.shieldSize() - lineWidth; let x2 = x0 + drawRadius; let x4 = (x0 + x8) / 2; @@ -302,30 +340,35 @@ function triangle(ctx, params, ref) { return width; } -function trapezoid(ctx, params, ref) { +function trapezoid( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { let shortSideUp = params.shortSideUp == undefined ? false : params.shortSideUp; - let angle = params.angle == undefined ? 0 : params.angle; + let sideAngle = params.sideAngle == undefined ? 0 : params.sideAngle; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; let angleSign = shortSideUp ? -1 : 1; - let sine = Math.sin(angle); - let cosine = Math.cos(angle); - let tangent = Math.tan(angle); + let sine = Math.sin(sideAngle); + let cosine = Math.cos(sideAngle); + let tangent = Math.tan(sideAngle); - let width = computeWidth(params, ref, "trapezoid"); + let width = computeWidth(r, params, ref, "trapezoid"); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; + let drawRadius = r.px(radius); let x0 = lineWidth; let x9 = width - lineWidth; - let y0 = shortSideUp ? CS - lineWidth : lineWidth; - let y3 = shortSideUp ? lineWidth : CS - lineWidth; + let y0 = shortSideUp ? r.shieldSize() - lineWidth : lineWidth; + let y3 = shortSideUp ? lineWidth : r.shieldSize() - lineWidth; let y1 = y0 + angleSign * drawRadius * (1 + sine); let y2 = y3 - angleSign * drawRadius * (1 - sine); @@ -359,18 +402,23 @@ function trapezoid(ctx, params, ref) { return width; } -function diamond(ctx, params, ref) { +function diamond( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; - let height = shapeHeight("diamond"); - let width = computeWidth(params, ref, "diamond"); + let height = shapeHeight(r, "diamond"); + let width = computeWidth(r, params, ref, "diamond"); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; + let drawRadius = r.px(radius); let x0 = lineWidth; let x8 = width - lineWidth; @@ -423,10 +471,15 @@ function diamond(ctx, params, ref) { return width; } -function pentagon(ctx, params, ref) { +function pentagon( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { let pointUp = params.pointUp == undefined ? true : params.pointUp; - let offset = params.offset == undefined ? 0 : params.offset; - let angle = params.angle == undefined ? 0 : params.angle; + let yOffset = params.yOffset == undefined ? 0 : params.yOffset; + let sideAngle = params.sideAngle == undefined ? 0 : params.sideAngle; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius1 = params.radius1 == undefined ? 0 : params.radius1; @@ -434,22 +487,22 @@ function pentagon(ctx, params, ref) { let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; let angleSign = pointUp ? -1 : 1; - let sine = Math.sin(angle); - let cosine = Math.cos(angle); - let tangent = Math.tan(angle); + let sine = Math.sin(sideAngle); + let cosine = Math.cos(sideAngle); + let tangent = Math.tan(sideAngle); - let width = computeWidth(params, ref, "pentagon"); + let width = computeWidth(r, params, ref, "pentagon"); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius1 = radius1 * PXR; - let drawRadius2 = radius2 * PXR; - let drawOffset = offset * PXR; + let drawRadius1 = r.px(radius1); + let drawRadius2 = r.px(radius2); + let drawOffset = r.px(yOffset); let x0 = lineWidth; let x8 = width - lineWidth; - let y0 = pointUp ? CS - lineWidth : lineWidth; - let y3 = pointUp ? lineWidth : CS - lineWidth; + let y0 = pointUp ? r.shieldSize() - lineWidth : lineWidth; + let y3 = pointUp ? lineWidth : r.shieldSize() - lineWidth; let y2 = y3 - angleSign * drawOffset; @@ -459,10 +512,10 @@ function pentagon(ctx, params, ref) { let offsetAngle = Math.atan(drawOffset / (x4 - x0)); - let halfComplementAngle1 = (Math.PI / 2 - offsetAngle + angle) / 2; + let halfComplementAngle1 = (Math.PI / 2 - offsetAngle + sideAngle) / 2; let halfComplementTangent1 = Math.tan(halfComplementAngle1); - let halfComplementAngle2 = (Math.PI / 2 - angle) / 2; + let halfComplementAngle2 = (Math.PI / 2 - sideAngle) / 2; let halfComplementTangent2 = Math.tan(halfComplementAngle2); let x1 = x0 + drawRadius1 * halfComplementTangent1 * sine; @@ -491,24 +544,29 @@ function pentagon(ctx, params, ref) { return width; } -function hexagonVertical(ctx, params, ref) { - let offset = params.offset == undefined ? 0 : params.offset; +function hexagonVertical( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { + let yOffset = params.yOffset == undefined ? 0 : params.yOffset; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; - let width = computeWidth(params, ref); + let width = computeWidth(r, params, ref); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; - let drawOffset = offset * PXR; + let drawRadius = r.px(radius); + let drawOffset = r.px(yOffset); let x0 = lineWidth; let x2 = width - lineWidth; let y0 = lineWidth; - let y5 = CS - lineWidth; + let y5 = r.shieldSize() - lineWidth; let x1 = (x0 + x2) / 2; let y1 = y0 + drawOffset; @@ -540,28 +598,33 @@ function hexagonVertical(ctx, params, ref) { return width; } -function hexagonHorizontal(ctx, params, ref) { - let angle = params.angle == undefined ? 0 : params.angle; +function hexagonHorizontal( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { + let sideAngle = params.sideAngle == undefined ? 0 : params.sideAngle; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; - let sine = Math.sin(angle); - let cosine = Math.cos(angle); - let tangent = Math.tan(angle); - let halfComplementTangent = Math.tan(Math.PI / 4 - angle / 2); + let sine = Math.sin(sideAngle); + let cosine = Math.cos(sideAngle); + let tangent = Math.tan(sideAngle); + let halfComplementTangent = Math.tan(Math.PI / 4 - sideAngle / 2); - let width = computeWidth(params, ref, "hexagonHorizontal"); + let width = computeWidth(r, params, ref, "hexagonHorizontal"); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; + let drawRadius = r.px(radius); let x0 = lineWidth; let x9 = width - lineWidth; let y0 = lineWidth; - let y6 = CS - lineWidth; + let y6 = r.shieldSize() - lineWidth; let y3 = (y0 + y6) / 2; @@ -601,29 +664,34 @@ function hexagonHorizontal(ctx, params, ref) { return width; } -function octagonVertical(ctx, params, ref) { - let offset = params.offset == undefined ? 0 : params.offset; - let angle = params.angle == undefined ? 0 : params.angle; +function octagonVertical( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { + let yOffset = params.yOffset == undefined ? 0 : params.yOffset; + let sideAngle = params.sideAngle == undefined ? 0 : params.sideAngle; let fill = params.fillColor == undefined ? "white" : params.fillColor; let outline = params.strokeColor == undefined ? "black" : params.strokeColor; let radius = params.radius == undefined ? 0 : params.radius; let outlineWidth = params.outlineWidth == undefined ? 1 : params.outlineWidth; - let sine = Math.sin(angle); - let cosine = Math.cos(angle); - let tangent = Math.tan(angle); + let sine = Math.sin(sideAngle); + let cosine = Math.cos(sideAngle); + let tangent = Math.tan(sideAngle); - let width = computeWidth(params, ref); + let width = computeWidth(r, params, ref); - let lineThick = outlineWidth * PXR; + let lineThick = r.px(outlineWidth); let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; - let drawOffset = offset * PXR; + let drawRadius = r.px(radius); + let drawOffset = r.px(yOffset); let x0 = lineWidth; let x10 = width - lineWidth; let y0 = lineWidth; - let y10 = CS - lineWidth; + let y10 = r.shieldSize() - lineWidth; let x1 = x0 + drawRadius * tangent * sine; let x5 = (x0 + x10) / 2; @@ -641,13 +709,15 @@ function octagonVertical(ctx, params, ref) { let offsetSine = Math.sin(offsetAngle); let offsetCosine = Math.cos(offsetAngle); - let halfComplementAngle = (Math.PI / 2 - angle - offsetAngle) / 2; + let halfComplementAngle = (Math.PI / 2 - sideAngle - offsetAngle) / 2; let halfComplementCosine = Math.cos(halfComplementAngle); let dx = - (drawRadius * Math.cos(angle + halfComplementAngle)) / halfComplementCosine; + (drawRadius * Math.cos(sideAngle + halfComplementAngle)) / + halfComplementCosine; let dy = - (drawRadius * Math.sin(angle + halfComplementAngle)) / halfComplementCosine; + (drawRadius * Math.sin(sideAngle + halfComplementAngle)) / + halfComplementCosine; let x2 = x3 + dx - drawRadius * cosine; let x4 = x3 + dx - drawRadius * offsetSine; @@ -681,17 +751,23 @@ function octagonVertical(ctx, params, ref) { return width; } -export function shapeHeight(name) { +export function shapeHeight(r: ShieldRenderingContext, name: string) { switch (name) { case "diamond": - return CS + 4 * PXR; + return r.shieldSize() + r.px(4); default: - return CS; + return r.shieldSize(); } } -export function draw(name, ctx, options, ref) { - return drawFunctions[name](ctx, options, ref); +export function draw( + r: ShieldRenderingContext, + name: string, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +): number { + return drawFunctions[name](r, ctx, params, ref); } //Register draw functions @@ -707,10 +783,14 @@ const fixedWidthDefinitions = {}; * @param {*} fxn callback to the implementing function. Takes two parameters, ref and options * @param {*} fixedWidth if set, indicates that this function draws to a fixed width */ -export function registerDrawFunction(name, fxn, fixedWidth) { +export function registerDrawFunction( + name: string, + fxn: ShapeDrawFunction, + fixedWidth?: number +) { drawFunctions[name] = fxn; if (fixedWidth !== undefined) { - fixedWidthDefinitions[name] = fixedWidth * PXR; + fixedWidthDefinitions[name] = fixedWidth; } } @@ -723,6 +803,10 @@ registerDrawFunction("hexagonVertical", hexagonVertical); registerDrawFunction("hexagonHorizontal", hexagonHorizontal); registerDrawFunction("octagonVertical", octagonVertical); registerDrawFunction("pentagon", pentagon); +registerDrawFunction("pill", pill); registerDrawFunction("roundedRectangle", roundedRectangle); registerDrawFunction("trapezoid", trapezoid); registerDrawFunction("triangle", triangle); + +//Built-in custom draw functions (weird stuff) +loadCustomShields(); diff --git a/shieldlib/src/shield_helper.d.ts b/shieldlib/src/shield_helper.d.ts new file mode 100644 index 000000000..177bf9a22 --- /dev/null +++ b/shieldlib/src/shield_helper.d.ts @@ -0,0 +1,146 @@ +import { ShieldDefinition, TextLayout } from "./types"; + +export declare function roundedRectTextConstraint(radius: number): TextLayout; +export declare function textConstraint(fxn: string): TextLayout; +export declare function ovalShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number +): ShieldDefinition; + +export declare function circleShield( + fillColor: string, + strokeColor: string, + textColor: string +): ShieldDefinition; + +export declare function roundedRectShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number, + radius: number +): ShieldDefinition; + +export declare function escutcheonDownShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function fishheadDownShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number +): ShieldDefinition; + +export declare function triangleDownShield( + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function trapezoidDownShield( + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function trapezoidUpShield( + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function diamondShield( + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function pentagonUpShield( + yOffset: number, + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius1: number, + radius2: number, + rectWidth: number +): ShieldDefinition; + +export declare function homePlateDownShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius1: number, + radius2: number, + rectWidth: number +): ShieldDefinition; + +export declare function homePlateUpShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius1: number, + radius2: number, + rectWidth: number +): ShieldDefinition; + +export declare function hexagonVerticalShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function hexagonHorizontalShield( + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function octagonVerticalShield( + yOffset: number, + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): ShieldDefinition; + +export declare function pillShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number +): ShieldDefinition; + +export function banneredShield( + baseDef: ShieldDefinition, + banners: string[], + bannerColor?: string +): ShieldDefinition; diff --git a/shieldlib/src/shield_helper.ts b/shieldlib/src/shield_helper.ts new file mode 100644 index 000000000..d6da83d26 --- /dev/null +++ b/shieldlib/src/shield_helper.ts @@ -0,0 +1,758 @@ +import { ShieldDefinition, TextLayout } from "./types"; + +/** + * Constrain the text to a rounded rectangle + * + * @param radius 1x pixel radius of the constraint corners + * @returns a constraint definition + */ +export function roundedRectTextConstraint(radius: number): TextLayout { + return { + constraintFunc: "roundedRect", + options: { + radius: radius, + }, + }; +} + +/** + * Constrain the text to a specified constraint type + * + * @returns a constraint definition + */ +export function textConstraint(fxn: string): TextLayout { + return { + constraintFunc: fxn, + }; +} + +/** + * Draws a shield with an ellipse background + * + * @param {*} fillColor - Color of ellipse background fill + * @param {*} strokeColor - Color of ellipse outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of ellipse (defaults to variable-width) + * @returns a shield definition object + */ +export function ovalShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + return { + shapeBlank: { + drawFunc: "ellipse", + params: { + fillColor, + strokeColor, + rectWidth, + }, + }, + textLayout: textConstraint("ellipse"), + padding: { + left: 2, + right: 2, + top: 2, + bottom: 2, + }, + textColor, + }; +} + +/** + * Draws a shield with circle background (special case of ovalShield) + * + * @param {*} fillColor - Color of circle background fill + * @param {*} strokeColor - Color of circle outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @returns a shield definition object + */ +export function circleShield( + fillColor: string, + strokeColor: string, + textColor: string +): Partial { + return ovalShield(fillColor, strokeColor, textColor, 20); +} + +/** + * Draws a shield with a rectangle background + * + * @param {*} fillColor - Color of rectangle background fill + * @param {*} strokeColor - Color of rectangle outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of rectangle (defaults to variable-width) + * @param {*} radius - Corner radius of rectangle (defaults to 2) + * @returns a shield definition object + */ +export function roundedRectShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number, + radius: number +): Partial { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + shapeBlank: { + drawFunc: "roundedRectangle", + params: { + fillColor, + strokeColor, + rectWidth, + radius, + }, + }, + textLayout: roundedRectTextConstraint(radius), + padding: { + left: 3, + right: 3, + top: 3, + bottom: 3, + }, + textColor, + }; +} + +/** + * Draws a shield with an escutcheon background, pointed downward + * + * @param {*} yOffset - Height of curved portion + * @param {*} fillColor - Color of escutcheon background fill + * @param {*} strokeColor - Color of escutcheon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of escutcheon (defaults to 0) + * @param {*} rectWidth - Width of escutcheon (defaults to variable-width) + * @returns a shield definition object + */ +export function escutcheonDownShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + radius = radius ?? 0; + return { + shapeBlank: { + drawFunc: "escutcheon", + params: { + yOffset, + fillColor, + strokeColor, + rectWidth, + radius, + outlineWidth: 1, + }, + }, + textLayout: roundedRectTextConstraint(radius), + padding: { + left: 2, + right: 2, + top: 2, + bottom: 0 + yOffset / 2, + }, + textColor, + }; +} + +/** + * Draws a shield with a fishhead background, pointed downward + * + * @param {*} fillColor - Color of fishhead background fill + * @param {*} strokeColor - Color of fishhead outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of fishhead (defaults to variable-width) + * @returns a shield definition object + */ +export function fishheadDownShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + return { + shapeBlank: { + drawFunc: "fishhead", + params: { + fillColor, + strokeColor, + rectWidth, + outlineWidth: 1, + }, + }, + textLayout: textConstraint("ellipse"), + padding: { + left: 1, + right: 1, + top: 1, + bottom: 5, + }, + textColor, + }; +} + +/** + * Draws a shield with a triangle background, pointed downward + * + * @param {*} fillColor - Color of triangle background fill + * @param {*} strokeColor - Color of triangle outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of triangle (defaults to 2) + * @param {*} rectWidth - Width of triangle (defaults to variable-width) + * @returns a shield definition object + */ +export function triangleDownShield( + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + + return { + shapeBlank: { + drawFunc: "triangle", + params: { + pointUp: false, + fillColor, + strokeColor, + rectWidth, + radius, + }, + }, + textLayout: textConstraint("triangleDown"), + padding: { + left: 1, + right: 1, + top: 2, + bottom: 1, + }, + textColor, + }; +} + +/** + * Draws a shield with a trapezoid background, with the short side on bottom + * + * @param {*} sideAngle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of trapezoid background fill + * @param {*} strokeColor - Color of trapezoid outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of trapezoid (defaults to 0) + * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) + * @returns a shield definition object + */ +export function trapezoidDownShield( + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + let angleInRadians = (sideAngle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius = radius ?? 0; + + return { + shapeBlank: { + drawFunc: "trapezoid", + params: { + sideAngle: angleInRadians, + fillColor, + strokeColor, + rectWidth, + radius, + }, + }, + textLayout: roundedRectTextConstraint(radius), + padding: { + left: 2 + 10 * Math.tan(angleInRadians), + right: 2 + 10 * Math.tan(angleInRadians), + top: 2, + bottom: 4, + }, + textColor, + }; +} + +/** + * Draws a shield with a trapezoid background, with the short side on top + * + * @param {*} sideAngle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of trapezoid background fill + * @param {*} strokeColor - Color of trapezoid outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of trapezoid (defaults to 0) + * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) + * @returns a shield definition object + */ +export function trapezoidUpShield( + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + let angleInRadians = (sideAngle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius = radius ?? 0; + return { + shapeBlank: { + drawFunc: "trapezoid", + params: { + shortSideUp: true, + sideAngle: angleInRadians, + fillColor, + strokeColor, + rectWidth, + radius, + }, + }, + textLayout: roundedRectTextConstraint(radius), + padding: { + left: 2 + 10 * Math.tan(angleInRadians), + right: 2 + 10 * Math.tan(angleInRadians), + top: 4, + bottom: 2, + }, + textColor, + }; +} + +/** + * Draws a shield with a diamond background + * + * @param {*} fillColor - Color of diamond background fill + * @param {*} strokeColor - Color of diamond outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of diamond (defaults to 2) + * @param {*} rectWidth - Width of diamond (defaults to variable-width) + * @returns a shield definition object + */ +export function diamondShield( + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + shapeBlank: { + drawFunc: "diamond", + params: { + fillColor, + strokeColor, + radius, + rectWidth, + }, + }, + textLayout: textConstraint("diamond"), + padding: { + left: 1, + right: 1, + top: 1, + bottom: 1, + }, + textColor, + }; +} + +/** + * Draws a shield with a pentagon background, pointed upward + * + * @param {*} yOffset - Height of diagonal edges + * @param {*} sideAngle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of pentagon background fill + * @param {*} strokeColor - Color of pentagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius1 - Corner radius of pointed side of pentagon (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 0) + * @param {*} rectWidth - Width of pentagon (defaults to variable-width) + * @returns a shield definition object + */ +export function pentagonUpShield( + yOffset: number, + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius1: number, + radius2: number, + rectWidth: number +): Partial { + let angleInRadians = (sideAngle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 0; + return { + shapeBlank: { + drawFunc: "pentagon", + params: { + yOffset, + sideAngle: angleInRadians, + fillColor, + strokeColor, + radius1, + radius2, + rectWidth, + }, + }, + textLayout: { + constraintFunc: "rect", + }, + padding: { + left: 2 + ((20 - yOffset) * Math.tan(angleInRadians)) / 2, + right: 2 + ((20 - yOffset) * Math.tan(angleInRadians)) / 2, + top: 1 + yOffset / 2, + bottom: 3, + }, + textColor, + }; +} + +/** + * Draws a shield with a home plate background, pointed downward + * + * @param {*} yOffset - Height of diagonal edges + * @param {*} fillColor - Color of home plate background fill + * @param {*} strokeColor - Color of home plate outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) + * @param {*} rectWidth - Width of home plate (defaults to variable-width) + * @returns a shield definition object + */ +export function homePlateDownShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius1: number, + radius2: number, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 2; + return { + shapeBlank: { + drawFunc: "pentagon", + params: { + pointUp: false, + yOffset, + sideAngle: 0, + fillColor, + strokeColor, + radius1, + radius2, + rectWidth, + }, + }, + textLayout: roundedRectTextConstraint(radius2), + padding: { + left: 2, + right: 2, + top: 2, + bottom: 1 + yOffset, + }, + textColor, + }; +} + +/** + * Draws a shield with a home plate background, pointed upward + * + * @param {*} yOffset - Height of diagonal edges + * @param {*} fillColor - Color of home plate background fill + * @param {*} strokeColor - Color of home plate outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) + * @param {*} rectWidth - Width of home plate (defaults to variable-width) + * @returns a shield definition object + */ +export function homePlateUpShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius1: number, + radius2: number, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 2; + return { + shapeBlank: { + drawFunc: "pentagon", + params: { + pointUp: true, + yOffset, + sideAngle: 0, + fillColor, + strokeColor, + radius1, + radius2, + rectWidth, + }, + }, + textLayout: roundedRectTextConstraint(radius2), + padding: { + left: 2, + right: 2, + top: 1 + yOffset, + bottom: 2, + }, + textColor, + }; +} + +/** + * Draws a shield with a vertically-aligned hexagon background + * + * @param {*} yOffset - Height of diagonal edges + * @param {*} fillColor - Color of hexagon background fill + * @param {*} strokeColor - Color of hexagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of hexagon (defaults to 2) + * @param {*} rectWidth - Width of hexagon (defaults to variable-width) + * @returns a shield definition object + */ +export function hexagonVerticalShield( + yOffset: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + shapeBlank: { + drawFunc: "hexagonVertical", + params: { + yOffset, + fillColor, + strokeColor, + radius, + rectWidth, + }, + }, + textLayout: roundedRectTextConstraint(radius), + padding: { + left: 2, + right: 2, + top: 1 + yOffset, + bottom: 1 + yOffset, + }, + textColor, + }; +} + +/** + * Draws a shield with a horizontally-aligned hexagon background + * + * @param {*} sideAngle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of hexagon background fill + * @param {*} strokeColor - Color of hexagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of hexagon (defaults to 2) + * @param {*} rectWidth - Width of hexagon (defaults to variable-width) + * @returns a shield definition object + */ +export function hexagonHorizontalShield( + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + let angleInRadians = (sideAngle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + shapeBlank: { + drawFunc: "hexagonHorizontal", + params: { + sideAngle: angleInRadians, + fillColor, + strokeColor, + radius, + rectWidth, + }, + }, + textLayout: textConstraint("ellipse"), + padding: { + left: 3, + right: 3, + top: 2, + bottom: 2, + }, + textColor, + }; +} + +/** + * Draws a shield with an octagon background + * + * @param {*} yOffset - Height of diagonal edges + * @param {*} sideAngle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of octagon background fill + * @param {*} strokeColor - Color of octagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of octagon (defaults to 2) + * @param {*} rectWidth - Width of octagon (defaults to variable-width) + * @returns a shield definition object + */ +export function octagonVerticalShield( + yOffset: number, + sideAngle: number, + fillColor: string, + strokeColor: string, + textColor: string, + radius: number, + rectWidth: number +): Partial { + let angleInRadians = (sideAngle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + shapeBlank: { + drawFunc: "octagonVertical", + params: { + yOffset, + sideAngle: angleInRadians, + fillColor, + strokeColor, + radius, + rectWidth, + }, + }, + textLayout: textConstraint("ellipse"), + padding: { + left: 2, + right: 2, + top: 2, + bottom: 2, + }, + textColor, + }; +} + +/** + * Draws a shield with a pill-shaped background + * + * @param {*} fillColor - Color of pill background fill + * @param {*} strokeColor - Color of pill outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of pill (defaults to variable-width) + * @returns a shield definition object + */ +export function pillShield( + fillColor: string, + strokeColor: string, + textColor: string, + rectWidth: number +): Partial { + textColor = textColor ?? strokeColor; + return { + shapeBlank: { + drawFunc: "pill", + params: { + fillColor, + strokeColor, + rectWidth, + }, + }, + textLayout: textConstraint("ellipse"), + padding: { + left: 2, + right: 2, + top: 2, + bottom: 2, + }, + textColor, + }; +} + +/** + * Adds banner text above a shield + * + * @param {*} baseDef - Shield definition object + * @param {*} banners - Array of strings to be displayed above shield + * @returns a shield definition object + */ +export function banneredShield( + baseDef: ShieldDefinition, + banners: string[], + bannerColor?: string +): ShieldDefinition { + return { + banners, + bannerTextColor: bannerColor, + ...baseDef, + }; +} + +/** + * Draws a circle icon inside a black-outlined white square shield + * + * @param {*} fillColor - Color of circle icon background fill + * @param {*} strokeColor - Color of circle icon outline + * @returns a shield definition object + */ +export function paBeltShield( + fillColor: string, + strokeColor: string +): Partial { + return { + notext: true, + shapeBlank: { + drawFunc: "paBelt", + params: { + fillColor, + strokeColor, + }, + }, + }; +} + +/** + * Draws a Branson, Missouri route shield + * + * @param {*} fillColor - Color of rectangle icon background fill + * @param {*} strokeColor - Color of rectangle icon outline + * @returns a shield definition object + */ +export function bransonRouteShield( + fillColor: string, + strokeColor: string +): Partial { + return { + notext: true, + shapeBlank: { + drawFunc: "branson", + params: { + fillColor, + strokeColor, + }, + }, + }; +} diff --git a/shieldlib/src/shield_renderer.d.ts b/shieldlib/src/shield_renderer.d.ts new file mode 100644 index 000000000..a136a7c2d --- /dev/null +++ b/shieldlib/src/shield_renderer.d.ts @@ -0,0 +1,77 @@ +import { + Map, + MapStyleImageMissingEvent, + StyleImage, + StyleImageMetadata, +} from "maplibre-gl"; +import { + Bounds, + DebugOptions, + GraphicsFactory, + RouteParser, + ShapeBlankParams, + ShieldDefinitions, + ShieldOptions, + ShieldSpecification, + SpriteRepository, + StringPredicate, +} from "./types"; +export declare class ShieldRenderingContext { + shieldDef: ShieldDefinitions; + options: ShieldOptions; + debugOptions: DebugOptions; + gfxFactory: GraphicsFactory; + spriteRepo: SpriteRepository; + private _emptySpriteCache; + emptySprite(): CanvasRenderingContext2D; + px(pixels: number): number; + shieldSize(): number; +} +export declare type ShapeDrawFunction = ( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref: string +) => void; +export declare class AbstractShieldRenderer { + private _shieldPredicate; + private _networkPredicate; + private _routeParser; + private _renderContext; + constructor(routeParser: RouteParser); + protected setShields(shieldSpec: ShieldSpecification): void; + debugOptions(debugOptions: DebugOptions): AbstractShieldRenderer; + filterImageID(shieldPredicate: StringPredicate): AbstractShieldRenderer; + filterNetwork(networkPredicate: StringPredicate): AbstractShieldRenderer; + graphicsFactory(gfxFactory: GraphicsFactory): AbstractShieldRenderer; + renderOnMaplibreGL(map: Map): AbstractShieldRenderer; + renderOnRepository(repo: SpriteRepository): AbstractShieldRenderer; + getStyleImageMissingHandler(): (e: MapStyleImageMissingEvent) => void; + getGraphicForRoute( + network: string, + ref: string, + name: string + ): CanvasRenderingContext2D; + emptySprite(): CanvasRenderingContext2D; + createGraphics(bounds: Bounds): CanvasRenderingContext2D; + pixelRatio(): number; +} +export declare class ShieldRenderer extends AbstractShieldRenderer { + constructor(shieldSpec: ShieldSpecification, routeParser: RouteParser); +} +export declare class URLShieldRenderer extends AbstractShieldRenderer { + constructor(shieldsURL: URL, routeParser: RouteParser); + setShieldURL(shieldsURL: URL): Promise; +} +export declare class InMemorySpriteRepository implements SpriteRepository { + sprites: {}; + getSprite(spriteID: string): StyleImage; + hasSprite(spriteID: string): boolean; + putSprite( + spriteID: string, + image: ImageData, + options: StyleImageMetadata, + update: boolean + ): void; +} +export {}; diff --git a/shieldlib/src/shield_renderer.ts b/shieldlib/src/shield_renderer.ts new file mode 100644 index 000000000..87f72cb55 --- /dev/null +++ b/shieldlib/src/shield_renderer.ts @@ -0,0 +1,336 @@ +import { + Map as MapLibre, + MapStyleImageMissingEvent, + StyleImage, + StyleImageMetadata, +} from "maplibre-gl"; +import { + Bounds, + DebugOptions, + GraphicsFactory, + RouteDefinition, + RouteParser, + ShapeBlankParams, + ShieldDefinitions, + ShieldOptions, + ShieldSpecification, + SpriteRepository, + StringPredicate, +} from "./types"; +import { + storeNoShield, + missingIconLoader, + generateShieldCtx, +} from "./shield.js"; +import { DOMGraphicsFactory } from "./document_graphics"; + +export class ShieldRenderingContext { + shieldDef: ShieldDefinitions; + options: ShieldOptions; + debugOptions?: DebugOptions; + gfxFactory: GraphicsFactory; + spriteRepo: SpriteRepository; + private _emptySpriteCache: CanvasRenderingContext2D; + + public emptySprite(): CanvasRenderingContext2D { + if (!this._emptySpriteCache) { + this._emptySpriteCache = this.gfxFactory.createGraphics({ + width: 1, + height: 1, + }); + } + return this._emptySpriteCache; + } + public px(pixels: number): number { + return pixels * this.gfxFactory.pixelRatio(); + } + public shieldSize(): number { + return this.px(this.options.shieldSize); + } +} + +export type ShapeDrawFunction = ( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + params: ShapeBlankParams, + ref?: string +) => number; + +class MaplibreGLSpriteRepository implements SpriteRepository { + map: MapLibre; + constructor(map: MapLibre) { + this.map = map; + } + getSprite(spriteID: string): StyleImage { + return this.map.style.getImage(spriteID); + } + putSprite( + spriteID: string, + image: ImageData, + options: Partial, + update: boolean + ): void { + if (update && this.map.listImages().includes(spriteID)) { + this.map.removeImage(spriteID); + this.map.addImage(spriteID, image); + } else { + this.map.addImage(spriteID, image, options); + } + } +} + +/** Base class for shield renderers. Shield renderers use a builder pattern to configure its options. */ +export class AbstractShieldRenderer { + private _shieldPredicate: StringPredicate = () => true; + private _networkPredicate: StringPredicate = () => true; + private _routeParser: RouteParser; + private _fontSpec: string; + private _map: MapLibre; + private _fontsLoaded: boolean = false; + /** Cache images that are loaded before fonts so they can be re-rendered later */ + private _preFontImageCache: Map = new Map(); + + /** @hidden */ + private _renderContext: ShieldRenderingContext; + private _shieldDefCallbacks = []; + + /** Create a shield renderer */ + constructor(routeParser: RouteParser) { + this._routeParser = routeParser; + this._renderContext = new ShieldRenderingContext(); + this._renderContext.gfxFactory = new DOMGraphicsFactory(); + } + + /** Specify which shields to draw and with what graphics */ + protected setShields(shieldSpec: ShieldSpecification) { + this._renderContext.options = shieldSpec.options; + this._renderContext.shieldDef = shieldSpec.networks; + this._fontSpec = "1em " + shieldSpec.options.shieldFont; + console.log("ShieldJSON loaded"); + if (this._map) { + this.reloadShieldsOnFontLoad(); + } + this._shieldDefCallbacks.forEach((callback) => + callback(shieldSpec.networks) + ); + } + + private onFontsLoaded() { + this._fontsLoaded = true; + if (this._preFontImageCache.size == 0) { + return; + } + console.log("Re-processing shields with loaded fonts"); + + // Loop through each previously-loaded shield and re-render it + for (let [id, routeDef] of this._preFontImageCache.entries()) { + missingIconLoader(this._renderContext, routeDef, id, true); + } + + this._preFontImageCache.clear(); + this._map.redraw(); + } + + /** Get the shield definitions */ + public getShieldDefinitions(): ShieldDefinitions { + return this._renderContext.shieldDef; + } + + /** Set debugging options */ + public debugOptions(debugOptions: DebugOptions): AbstractShieldRenderer { + this._renderContext.debugOptions = debugOptions; + return this; + } + + /** Set which unhandled sprite IDs this renderer handles */ + public filterImageID( + shieldPredicate: StringPredicate + ): AbstractShieldRenderer { + this._shieldPredicate = shieldPredicate; + return this; + } + + /** Set which network values this renderer handles */ + public filterNetwork( + networkPredicate: StringPredicate + ): AbstractShieldRenderer { + this._networkPredicate = networkPredicate; + return this; + } + + /** Set which graphics context to draw shields to */ + public graphicsFactory(gfxFactory: GraphicsFactory): AbstractShieldRenderer { + this._renderContext.gfxFactory = gfxFactory; + return this; + } + + /** Set which MaplibreGL map to handle shields for */ + public renderOnMaplibreGL(map: MapLibre): AbstractShieldRenderer { + this._map = map; + if (this._fontSpec) { + this.reloadShieldsOnFontLoad(); + } + this.renderOnRepository(new MaplibreGLSpriteRepository(map)); + map.on("styleimagemissing", this.getStyleImageMissingHandler()); + return this; + } + + private reloadShieldsOnFontLoad(): void { + if (!this._fontsLoaded && !document.fonts.check(this._fontSpec)) { + document.fonts.load(this._fontSpec).then(() => this.onFontsLoaded()); + } else { + this._fontsLoaded = true; + } + } + + /** Set a callback that fires when shield definitions are loaded */ + public onShieldDefLoad( + callback: (shields: ShieldDefinitions) => void + ): AbstractShieldRenderer { + if (this._renderContext.shieldDef) { + callback(this._renderContext.shieldDef); + } else { + this._shieldDefCallbacks.push(callback); + } + return this; + } + + /** Set the storage location for existing and generated sprite images */ + public renderOnRepository(repo: SpriteRepository): AbstractShieldRenderer { + if (!this._renderContext.spriteRepo) { + this._renderContext.spriteRepo = repo; + } + return this; + } + + /** + * Get the handler function for styleimagemissing event calls + * + * See [MapStyleImageMissingEvent](https://maplibre.org/maplibre-gl-js/docs/API/types/maplibregl.MapStyleImageMissingEvent/) for more details. + **/ + public getStyleImageMissingHandler() { + return (e: MapStyleImageMissingEvent) => { + try { + if (!this._shieldPredicate(e.id)) { + return; + } + let routeDef: RouteDefinition = this._routeParser.parse(e.id); + if (!this._networkPredicate(routeDef.network)) { + storeNoShield(this._renderContext, e.id); + return; + } + if (routeDef) { + if (!this._fontsLoaded && routeDef.ref) { + this._preFontImageCache.set(e.id, routeDef); + } + missingIconLoader(this._renderContext, routeDef, e.id, false); + } + } catch (err) { + console.error(`Exception while loading image ‘${e?.id}’:\n`, err); + } + }; + } + + /** Get the graphic for a specified route */ + public getGraphicForRoute(network: string, ref: string, name: string) { + return generateShieldCtx(this._renderContext, { + network, + ref, + name, + }); + } + + /** Get a blank route shield sprite in the default size */ + public emptySprite(): CanvasRenderingContext2D { + return this._renderContext.emptySprite(); + } + + /** Get a blank route shield graphics context in a specified size */ + public createGraphics(bounds: Bounds): CanvasRenderingContext2D { + return this._renderContext.gfxFactory.createGraphics(bounds); + } + + /** Get the current pixel ration (1x/2x) */ + public pixelRatio(): number { + return this._renderContext.px(1); + } +} + +/** + * A shield renderer configured from a JSON specification + * + * @example + * + * const shields = { + * "US:I": { + * textColor: Color.shields.white, + * spriteBlank: ["shield_us_interstate_2", "shield_us_interstate_3"], + * textLayout: textConstraint("southHalfEllipse"), + * padding: { + * left: 4, + * right: 4, + * top: 6, + * bottom: 5, + * }, + * } + * }; + * + * const shieldRenderer = new ShieldRenderer(shields, routeParser) + * .filterImageID(shieldPredicate) + * .filterNetwork(networkPredicate) + * .renderOnMaplibreGL(map) + * .onShieldDefLoad((shields) => afterShieldRendererLoads(shields)); //Post config + */ +export class ShieldRenderer extends AbstractShieldRenderer { + constructor(shieldSpec: ShieldSpecification, routeParser: RouteParser) { + super(routeParser); + this.setShields(shieldSpec); + } +} + +/** + * A shield renderer configured from a URL containing a JSON specification + * + * @example + * + * const shieldRenderer = new URLShieldRenderer("shields.json", routeParser) + * .filterImageID(shieldPredicate) + * .filterNetwork(networkPredicate) + * .renderOnMaplibreGL(map) + * .onShieldDefLoad((shields) => afterShieldRendererLoads(shields)); //Post config + **/ +export class URLShieldRenderer extends AbstractShieldRenderer { + constructor( + /** URL containing the JSON shield definition */ + shieldsURL: URL, + /** Function that extracts route identification from a sprite string */ + routeParser: RouteParser + ) { + super(routeParser); + this.setShieldURL(shieldsURL); + } + + /** Set the URL containing the shield specification */ + private async setShieldURL(shieldsURL: URL) { + await fetch(shieldsURL) + .then((res) => res.json()) + .then((json) => super.setShields(json)) + .catch((err) => console.error(err)); + } +} + +/** @hidden Used for testing */ +export class InMemorySpriteRepository implements SpriteRepository { + sprites = {}; + public getSprite(spriteID: string): StyleImage { + return this.sprites[spriteID]; + } + public hasSprite(spriteID: string): boolean { + return spriteID in this.sprites; + } + public putSprite(spriteID: string, image: ImageData): void { + this.sprites[spriteID] = image; + } +} + +const localRepo = new InMemorySpriteRepository(); diff --git a/src/js/shield_text.js b/shieldlib/src/shield_text.ts similarity index 50% rename from src/js/shield_text.js rename to shieldlib/src/shield_text.ts index 2ac7ecbd5..8d19a92a9 100644 --- a/src/js/shield_text.js +++ b/shieldlib/src/shield_text.ts @@ -1,18 +1,49 @@ "use strict"; -import * as Color from "../constants/color.js"; import * as Gfx from "./screen_gfx.js"; -import * as ShieldDef from "./shield_defs.js"; - -export const PXR = Gfx.getPixelRatio(); +import { ShieldRenderingContext } from "./shield_renderer.js"; +import { + BoxPadding, + Dimension, + ShieldDefinition, + TextLayout, + TextLayoutParameters, +} from "./types.js"; const VerticalAlignment = { Middle: "middle", Top: "top", Bottom: "bottom", +} as const; + +type VerticalAlignmentType = + (typeof VerticalAlignment)[keyof typeof VerticalAlignment]; + +type TextLayoutScaler = ( + availSize: Dimension, + textSize: Dimension, + options?: TextLayoutParameters +) => TextTransform; + +interface TextTransform { + scale: number; + valign: VerticalAlignmentType; +} + +export interface TextPlacement { + xBaseline: number; + yBaseline: number; + fontPx: number; +} + +let noPadding: BoxPadding = { + top: 0, + bottom: 0, + left: 0, + right: 0, }; -function ellipseScale(spaceBounds, textBounds) { +function ellipseScale(spaceBounds: Dimension, textBounds: Dimension): number { //Math derived from https://mathworld.wolfram.com/Ellipse-LineIntersection.html var a = spaceBounds.width; var b = spaceBounds.height; @@ -23,14 +54,20 @@ function ellipseScale(spaceBounds, textBounds) { return (a * b) / Math.sqrt(a * a * y0 * y0 + b * b * x0 * x0); } -function ellipseTextConstraint(spaceBounds, textBounds) { +function ellipseTextConstraint( + spaceBounds: Dimension, + textBounds: Dimension +): TextTransform { return { scale: ellipseScale(spaceBounds, textBounds), valign: VerticalAlignment.Middle, }; } -function southHalfEllipseTextConstraint(spaceBounds, textBounds) { +function southHalfEllipseTextConstraint( + spaceBounds: Dimension, + textBounds: Dimension +): TextTransform { return { scale: ellipseScale(spaceBounds, { //Turn ellipse 90 degrees @@ -41,7 +78,10 @@ function southHalfEllipseTextConstraint(spaceBounds, textBounds) { }; } -function rectTextConstraint(spaceBounds, textBounds) { +function rectTextConstraint( + spaceBounds: Dimension, + textBounds: Dimension +): TextTransform { var scaleHeight = spaceBounds.height / textBounds.height; var scaleWidth = spaceBounds.width / textBounds.width; @@ -51,7 +91,11 @@ function rectTextConstraint(spaceBounds, textBounds) { }; } -function roundedRectTextConstraint(spaceBounds, textBounds, options) { +function roundedRectTextConstraint( + spaceBounds: Dimension, + textBounds: Dimension, + options: TextLayoutParameters +): TextTransform { //Shrink space bounds so that corners hit the arcs let constraintRadius = 2; if (options !== undefined && options.radius !== undefined) { @@ -67,7 +111,10 @@ function roundedRectTextConstraint(spaceBounds, textBounds, options) { ); } -function diamondTextConstraint(spaceBounds, textBounds) { +function diamondTextConstraint( + spaceBounds: Dimension, + textBounds: Dimension +): TextTransform { let a = spaceBounds.width; let b = spaceBounds.height; @@ -80,17 +127,30 @@ function diamondTextConstraint(spaceBounds, textBounds) { }; } -function triangleDownTextConstraint(spaceBounds, textBounds) { +function triangleDownTextConstraint( + spaceBounds: Dimension, + textBounds: Dimension +): TextTransform { return { scale: diamondTextConstraint(spaceBounds, textBounds).scale, valign: VerticalAlignment.Top, }; } +// Warning!!! Hack!!! +function isRunningInWebKit(): boolean { + if (typeof window === "undefined") { + return false; + } + const userAgent = window.navigator.userAgent; + return /WebKit/i.test(userAgent) && !/Chrome/i.test(userAgent); +} + /** * Determines the position and font size to draw text so that it fits within * a bounding box. * + * @param {*} r - rendering context * @param {*} text - text to draw * @param {*} padding - top/bottom/left/right padding around text * @param {*} bounds - size of the overall graphics area @@ -98,37 +158,57 @@ function triangleDownTextConstraint(spaceBounds, textBounds) { * @param {*} maxFontSize - maximum font size * @returns JOSN object containing (X,Y) draw position and font size */ -function layoutShieldText(text, padding, bounds, textLayoutDef, maxFontSize) { - const PXR = Gfx.getPixelRatio(); - - var padTop = padding.top * PXR || 0; - var padBot = padding.bottom * PXR || 0; - var padLeft = padding.left * PXR || 0; - var padRight = padding.right * PXR || 0; - - var maxFont = maxFontSize * PXR; +export function layoutShieldText( + r: ShieldRenderingContext, + text: string, + padding: BoxPadding, + bounds: Dimension, + textLayoutDef: TextLayout, + maxFontSize: number = 14 +): TextPlacement { + let padTop = r.px(padding.top) || 0; + let padBot = r.px(padding.bottom) || 0; + let padLeft = r.px(padding.left) || 0; + let padRight = r.px(padding.right) || 0; + + let maxFont = r.px(maxFontSize); //Temporary canvas for text measurment - var ctx = Gfx.getGfxContext(bounds); + let ctx: CanvasRenderingContext2D = r.gfxFactory.createGraphics(bounds); - ctx.font = Gfx.shieldFont(Gfx.fontSizeThreshold); - ctx.textAlign = "center"; + ctx.font = Gfx.shieldFont(Gfx.fontSizeThreshold, r.options.shieldFont); + ctx.textAlign = "left"; ctx.textBaseline = "top"; - var metrics = ctx.measureText(text); + let metrics: TextMetrics = ctx.measureText(text); + + let textWidth: number = + Math.abs(metrics.actualBoundingBoxLeft) + + Math.abs(metrics.actualBoundingBoxRight); + let textHeight: number = + Math.abs(metrics.actualBoundingBoxDescent) + + Math.abs(metrics.actualBoundingBoxAscent); + + //Adjust for excess descender text height across browsers + textHeight *= 0.9; - var textWidth = metrics.width; - var textHeight = metrics.actualBoundingBoxDescent; + //Adjust for excess text height measured in Webkit engine specifically + if (isRunningInWebKit()) { + textHeight *= 0.54; + } - var availHeight = bounds.height - padTop - padBot; - var availWidth = bounds.width - padLeft - padRight; + let availHeight: number = bounds.height - padTop - padBot; + let availWidth: number = bounds.width - padLeft - padRight; - var xBaseline = padLeft + availWidth / 2; + let xBaseline: number = padLeft + availWidth / 2; let textLayoutFunc = drawTextFunctions[textLayoutDef.constraintFunc]; - let textConstraint = textLayoutFunc( - { height: availHeight, width: availWidth }, - { height: textHeight, width: textWidth }, + let spaceAvail: Dimension = { height: availHeight, width: availWidth }; + let measuredTextBounds: Dimension = { height: textHeight, width: textWidth }; + + let textConstraint: TextTransform = textLayoutFunc( + spaceAvail, + measuredTextBounds, textLayoutDef.options ); @@ -138,21 +218,24 @@ function layoutShieldText(text, padding, bounds, textLayoutDef, maxFontSize) { Gfx.fontSizeThreshold * textConstraint.scale ); - ctx.font = Gfx.shieldFont(fontSize); - ctx.textAlign = "center"; + ctx.font = Gfx.shieldFont(fontSize, r.options.shieldFont); + ctx.textAlign = "left"; ctx.textBaseline = "top"; metrics = ctx.measureText(text); - textHeight = metrics.actualBoundingBoxDescent; + textHeight = + Math.abs(metrics.actualBoundingBoxDescent) + + Math.abs(metrics.actualBoundingBoxAscent); - var yBaseline; + let yBaseline: number; switch (textConstraint.valign) { case VerticalAlignment.Top: - yBaseline = padTop; + yBaseline = padTop + metrics.actualBoundingBoxAscent; break; case VerticalAlignment.Bottom: - yBaseline = padTop + availHeight - textHeight; + yBaseline = + padTop + availHeight - textHeight + metrics.actualBoundingBoxAscent; break; case VerticalAlignment.Middle: default: @@ -161,36 +244,50 @@ function layoutShieldText(text, padding, bounds, textLayoutDef, maxFontSize) { } return { - xBaseline: xBaseline, - yBaseline: yBaseline, + xBaseline, + yBaseline, fontPx: fontSize, }; } -const defaultDefForLayout = { +const defaultDefForLayout: Partial = { padding: { top: 0, bottom: 0, left: 0, right: 0, }, + shapeBlank: { + drawFunc: "rect", + params: { + fillColor: "white", + strokeColor: "black", + }, + }, }; /** * Determines the position and font size to draw text so that it fits within * a bounding box. * + * @param {*} r - rendering context * @param {*} text - text to draw * @param {*} def - shield definition * @param {*} bounds - size of the overall graphics area * @returns JOSN object containing (X,Y) draw position and font size */ -export function layoutShieldTextFromDef(text, def, bounds) { +export function layoutShieldTextFromDef( + r: ShieldRenderingContext, + text: string, + def: Partial, + bounds: Dimension +): TextPlacement { + //FIX if (def == null) { def = defaultDefForLayout; } - var padding = def.padding || {}; + var padding = def.padding || noPadding; var textLayoutDef = { constraintFunc: "rect", @@ -206,21 +303,25 @@ export function layoutShieldTextFromDef(text, def, bounds) { maxFontSize = Math.min(maxFontSize, def.maxFontSize); // shield definition cannot set max size higher than default } - return layoutShieldText(text, padding, bounds, textLayoutDef, maxFontSize); + return layoutShieldText(r, text, padding, bounds, textLayoutDef, maxFontSize); } /** * Draw text on a shield * + * @param {*} r - rendering context * @param {*} ctx - graphics context to draw to * @param {*} text - text to draw * @param {*} textLayout - location to draw text */ -export function drawShieldText(ctx, text, textLayout) { +export function renderShieldText( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + text: string, + textLayout: TextPlacement +): void { //Text color is set by fillStyle - ctx.textAlign = "center"; - ctx.textBaseline = "top"; - ctx.font = Gfx.shieldFont(textLayout.fontPx); + configureShieldText(r, ctx, textLayout); ctx.fillText(text, textLayout.xBaseline, textLayout.yBaseline); } @@ -228,94 +329,49 @@ export function drawShieldText(ctx, text, textLayout) { /** * Draw drop shadow for text on a shield * + * @param {*} r - rendering context * @param {*} ctx - graphics context to draw to * @param {*} text - text to draw * @param {*} textLayout - location to draw text */ -export function drawShieldHaloText(ctx, text, textLayout) { +export function drawShieldHaloText( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + text: string, + textLayout: TextPlacement +): void { //Stroke color is set by strokeStyle - ctx.textAlign = "center"; - ctx.textBaseline = "top"; - ctx.font = Gfx.shieldFont(textLayout.fontPx); + configureShieldText(r, ctx, textLayout); - ctx.shadowColor = ctx.strokeStyle; + ctx.shadowColor = ctx.strokeStyle.toString(); ctx.shadowBlur = 0; - ctx.lineWidth = 2 * PXR; + ctx.lineWidth = r.px(2); ctx.strokeText(text, textLayout.xBaseline, textLayout.yBaseline); ctx.shadowColor = null; ctx.shadowBlur = null; } -/** - * Draw text on a modifier plate above a shield - * - * @param {*} ctx - graphics context to draw to - * @param {*} text - text to draw - * @param {*} bannerIndex - plate position to draw, 0=top, incrementing - */ -export function drawBannerText(ctx, text, bannerIndex) { - var textLayout = layoutShieldTextFromDef(text, null, { - width: ctx.canvas.width, - height: ShieldDef.bannerSizeH - ShieldDef.bannerPadding, - }); - - ctx.fillStyle = "black"; - - ctx.font = Gfx.shieldFont(textLayout.fontPx); - ctx.textBaseline = "top"; +function configureShieldText( + r: ShieldRenderingContext, + ctx: CanvasRenderingContext2D, + textLayout: TextPlacement +): void { ctx.textAlign = "center"; - - ctx.fillText( - text, - textLayout.xBaseline, - textLayout.yBaseline + - bannerIndex * ShieldDef.bannerSizeH - - ShieldDef.bannerPadding - ); -} - -/** - * Draw drop shadow for text on a modifier plate above a shield - * - * @param {*} ctx - graphics context to draw to - * @param {*} text - text to draw - * @param {*} bannerIndex - plate position to draw, 0=top, incrementing - */ -export function drawBannerHaloText(ctx, text, bannerIndex) { - var textLayout = layoutShieldTextFromDef(text, null, { - width: ctx.canvas.width, - height: ShieldDef.bannerSizeH - ShieldDef.bannerPadding, - }); - - (ctx.shadowColor = Color.backgroundFill), (ctx.strokeStyle = ctx.shadowColor); - ctx.font = Gfx.shieldFont(textLayout.fontPx); ctx.textBaseline = "top"; - ctx.textAlign = "center"; - ctx.shadowBlur = 0; - ctx.lineWidth = 2 * PXR; - - ctx.strokeText( - text, - textLayout.xBaseline, - textLayout.yBaseline + - bannerIndex * ShieldDef.bannerSizeH - - ShieldDef.bannerPadding - ); - ctx.shadowColor = null; - ctx.shadowBlur = null; + ctx.font = Gfx.shieldFont(textLayout.fontPx, r.options.shieldFont); } -export function calculateTextWidth(text, fontSize) { - var ctx = Gfx.getGfxContext({ width: 1, height: 1 }); //dummy canvas - ctx.font = Gfx.shieldFont(fontSize); +export function calculateTextWidth( + r: ShieldRenderingContext, + text: string, + fontSize: number +): number { + var ctx = r.emptySprite(); //dummy canvas + ctx.font = Gfx.shieldFont(fontSize, r.options.shieldFont); return Math.ceil(ctx.measureText(text).width); } -export function drawText(name, options, ref) { - return drawTextFunctions[name](options, ref); -} - //Register text draw functions const drawTextFunctions = {}; @@ -325,7 +381,7 @@ const drawTextFunctions = {}; * @param {*} name name of the function as referenced by the shield definition * @param {*} fxn callback to the implementing function. Takes two parameters, ref and options */ -function registerDrawTextFunction(name, fxn) { +function registerDrawTextFunction(name: string, fxn: TextLayoutScaler): void { drawTextFunctions[name] = fxn; } diff --git a/shieldlib/src/types.d.ts b/shieldlib/src/types.d.ts new file mode 100644 index 000000000..a91903395 --- /dev/null +++ b/shieldlib/src/types.d.ts @@ -0,0 +1,68 @@ +import { StyleImage, StyleImageMetadata } from "maplibre-gl"; +export interface RouteDefinition { + network: string; + ref: string; + name: string; +} +export interface ShieldDefinition { + spriteBlank: string[]; + textColor: string; + padding: { + left: number; + right: number; + top: number; + bottom: number; + }; +} +export type StringPredicate = (spriteID: string) => boolean; +export interface RouteParser { + parse(spriteID: string): RouteDefinition; + format(network: string, ref: string, name: string): string; +} +export interface SpriteProducer { + getSprite(spriteID: string): StyleImage; +} +export interface SpriteConsumer { + putSprite( + spriteID: string, + image: ImageData, + options: Partial, + update: boolean + ): void; +} +export type SpriteRepository = SpriteProducer & SpriteConsumer; +export interface ShieldDefinitions { + shield: { + [key: string]: ShieldDefinition; + }; +} +export interface DebugOptions {} +export interface ShieldOptions { + bannerTextColor: string; + bannerTextHaloColor: string; + shieldFont: string; + shieldSize: number; +} +export interface ShieldSpecification { + networks: ShieldDefinitions; + options: ShieldOptions; +} +export type Bounds = { + width: number; + height: number; +}; +export type GfxFactory = (bounds: Bounds) => CanvasRenderingContext2D; +export interface GraphicsFactory { + createGraphics(bounds: Bounds): CanvasRenderingContext2D; + /** + * Get pixel ratio MapLibre is using for icons based on devicePixelRatio (DPR). + * MapLibre uses 2x sprites whenever DPR > 1 and 1x sprites otherwise. + * So this function will always return 1 or 2 as appropriate based on DPR. + */ + pixelRatio(): number; +} + +export interface Dimension { + width: number; + height: number; +} diff --git a/shieldlib/src/types.ts b/shieldlib/src/types.ts new file mode 100644 index 000000000..b008a5cea --- /dev/null +++ b/shieldlib/src/types.ts @@ -0,0 +1,261 @@ +import { + StyleImage, + StyleImageInterface, + StyleImageMetadata, +} from "maplibre-gl"; + +/** Defines the set of routes that a shield applies to */ +export interface RouteDefinition { + /** Only match routes with this network value */ + network: string; + /** If set, only match routes with this ref value */ + ref?: string; + /** If set, only match routes with this name value */ + name?: string; +} + +/** Enforce a requirement that one field OR another field must be specified, but not both */ +export type Exclusive = + | (T & { [P in keyof U]?: never }) + | (U & { [P in keyof T]?: never }); + +/** Parameters that apply to all types of shield definitions */ +export interface ShieldDefinitionBase { + /** Color of text drawn on a shield */ + textColor?: string; + /** Color of the halo drawn around text */ + textHaloColor?: string; + /** Color of banner text */ + bannerTextColor?: string; + /** Color of banner text halo */ + bannerTextHaloColor?: string; + /** Padding around shield text */ + padding?: BoxPadding; + /** Algorithm for expanding text to fill a shield background */ + textLayout?: TextLayout; + /** Banners to be drawn above a shield */ + banners?: string[]; + /** If true, no next should be drawn on this shield */ + notext?: boolean; + /** Maximum size of shield text */ + maxFontSize?: number; + /** ref values that can be mapped from names */ + refsByName?: Map; + /** hard-code the shield text to this value */ + ref?: string; + /** Transpose numbering system, for example "roman" for Roman numerals */ + numberingSystem?: string; + /** Reflect this shield vertically */ + verticalReflect: boolean; + /** Perform a color lighten operation with this color */ + colorLighten: string; + /** Perform a color darken operation with this color */ + colorDarken: string; + /** Provide a different shield style for specific name values */ + overrideByName: Map; + /** Provide a different shield style for specific ref values */ + overrideByRef: Map; + /** Provide a different shield style when there's no ref value */ + noref: ShieldDefinition; +} + +/** + * Define how the renderer should draw the shield for various routes + * + * @example + * const shieldsDefinition = { + * "US:I": { + * textColor: Color.shields.white, + * spriteBlank: ["shield_us_interstate_2", "shield_us_interstate_3"], + * textLayout: textConstraint("southHalfEllipse"), + * padding: { + * left: 4, + * right: 4, + * top: 6, + * bottom: 5, + * } + * }; + */ +export type ShieldDefinition = Exclusive< + { spriteBlank: string[] }, + { shapeBlank: ShapeDefinition } +> & + ShieldDefinitionBase; + +/** Define a shield which is created by drawing a shape, optionally with text on top */ +export interface ShapeDefinition { + /** Which shape to draw */ + drawFunc: string; + /** Parameters for drawing the shape */ + params: ShapeBlankParams; +} + +/** Rectangular padding values */ +export interface BoxPadding { + /** Minimum padding to the left of the text */ + left: number; + /** Minimum padding to the right of the text */ + right: number; + /** Minimum padding above the text */ + top: number; + /** Minimum padding below the text */ + bottom: number; +} + +/** Parameters for drawing shield shapes */ +export interface ShapeBlankParams { + /** Fill color of the shape */ + fillColor: string; + /** Stroke (border) color */ + strokeColor: string; + /** Width of the shape */ + rectWidth?: number; + /** Radius of the shape's corners */ + radius?: number; + /** Radius of the shape's first corner. This is used for shapes that can specify multiple radius values */ + radius1?: number; + /** Radius of the shape's second corner. This is used for shapes that can specify multiple radius values */ + radius2?: number; + /** Distance from top or bottom edge to vertices. Higher number means pointier top and/or bottom */ + yOffset?: number; + /** Width of the shape's outline */ + outlineWidth?: number; + /** Specify whether the pointy end of the shape is on top */ + pointUp?: boolean; + /** Specify whether the short side of the shape is on top */ + shortSideUp?: boolean; + /** Specify the angle at which the sides of the shape deviate from vertical. Higher number means pointier sides */ + sideAngle?: number; +} + +/** Definition for laying out text on a shield background */ +export interface TextLayout { + constraintFunc: string; + options?: TextLayoutParameters; +} + +/** Options for text layout on a shield */ +export interface TextLayoutParameters { + radius: number; +} + +/** + * A predicate which determines whether to draw a shield for a particular sprite ID. + * This allows the library to consume a subset of sprite IDs passed in from maplibre + * while allowing other code to handle other sprite IDs. + */ +export type StringPredicate = (spriteID: string) => boolean; + +/** RouteParser unpacks a route definition from a sprite image string */ +export interface RouteParser { + parse(spriteID: string): RouteDefinition; + format(network: string, ref: string, name: string): string; +} + +/** Retrieve a sprite graphic based on an ID */ +export interface SpriteProducer { + getSprite(spriteID: string): StyleImage; +} + +/** Store a sprite graphic based on an ID */ +export interface SpriteConsumer { + putSprite( + spriteID: string, + image: ImageData | StyleImageInterface, + options: Partial, + update: boolean + ): void; +} + +/** Respository that can store and retrieve sprite graphics */ +export type SpriteRepository = SpriteProducer & SpriteConsumer; + +/** Map of shield definitions that associates a network name to its rendering */ +export interface ShieldDefinitions { + shield: { + [key: string]: ShieldDefinition; + }; +} + +/** Additional debugging-only override options */ +export interface DebugOptions { + /** If set, draw a colored box around shield text constraint */ + shieldTextBboxColor?: string; + + /** If set, draw a halo of this color around text */ + shieldTextHaloColor?: string; +} + +/** Global options for shield rendering */ +export interface ShieldOptions { + /** Height of each specified banner above the shield */ + bannerHeight: number; + /** Padding between each banner */ + bannerPadding: number; + /** Color of text on the banner */ + bannerTextColor: string; + /** Color of halo on text on the banner */ + bannerTextHaloColor: string; + /** Browser font for banner text */ + shieldFont: string; + /** Default shield size in pixels at 1x */ + shieldSize: number; +} + +/** + * A user-supplied specification for rendering shields + * + * @example + * + * const shieldsSpecification = { + * shields: { + * "US:I": { + * textColor: Color.shields.white, + * spriteBlank: ["shield_us_interstate_2", "shield_us_interstate_3"], + * textLayout: textConstraint("southHalfEllipse"), + * padding: { + * left: 4, + * right: 4, + * top: 6, + * bottom: 5, + * }, + * } + * }, + * options: { + * bannerTextColor: "#000", + * bannerTextHaloColor: "#FFF", + * bannerHeight: 9, + * bannerPadding: 1, + * shieldFont: '"sans-serif-condensed", "Arial Narrow", sans-serif', + * shieldSize: 20, + * } + * }; + * + */ +export interface ShieldSpecification { + /** Shield definitions */ + networks: ShieldDefinitions; + /** Shield options */ + options: ShieldOptions; +} + +/** Rectangular bounds, in scaled pixels */ +export type Bounds = { + width: number; + height: number; +}; + +export interface GraphicsFactory { + createGraphics(bounds: Bounds): CanvasRenderingContext2D; + /** + * Get pixel ratio MapLibre is using for icons based on devicePixelRatio (DPR). + * MapLibre uses 2x sprites whenever DPR > 1 and 1x sprites otherwise. + * So this function will always return 1 or 2 as appropriate based on DPR. + */ + pixelRatio(): number; +} + +export interface Dimension { + width: number; + height: number; +} diff --git a/shieldlib/test/spec/shield.ts b/shieldlib/test/spec/shield.ts new file mode 100644 index 000000000..d8e4d7a23 --- /dev/null +++ b/shieldlib/test/spec/shield.ts @@ -0,0 +1,45 @@ +"use strict"; + +import { expect } from "chai"; +import { romanizeRef } from "../../src/shield"; + +describe("shield", function () { + describe("#romanizeRef", function () { + it("adds", function () { + expect(romanizeRef("0")).to.be.eql(""); + expect(romanizeRef("1")).to.be.eql("I"); + expect(romanizeRef("2")).to.be.eql("II"); + expect(romanizeRef("5")).to.be.eql("V"); + expect(romanizeRef("6")).to.be.eql("VI"); + expect(romanizeRef("10")).to.be.eql("X"); + expect(romanizeRef("11")).to.be.eql("XI"); + expect(romanizeRef("50")).to.be.eql("L"); + expect(romanizeRef("51")).to.be.eql("LI"); + expect(romanizeRef("60")).to.be.eql("LX"); + expect(romanizeRef("61")).to.be.eql("LXI"); + expect(romanizeRef("100")).to.be.eql("C"); + expect(romanizeRef("101")).to.be.eql("CI"); + expect(romanizeRef("111")).to.be.eql("CXI"); + expect(romanizeRef("555")).to.be.eql("DLV"); + expect(romanizeRef("5000")).to.be.eql("MMMMM"); + }); + it("subtracts", function () { + expect(romanizeRef("4")).to.be.eql("IV"); + expect(romanizeRef("9")).to.be.eql("IX"); + expect(romanizeRef("14")).to.be.eql("XIV"); + expect(romanizeRef("40")).to.be.eql("XL"); + expect(romanizeRef("49")).to.be.eql("XLIX"); + expect(romanizeRef("90")).to.be.eql("XC"); + expect(romanizeRef("99")).to.be.eql("XCIX"); + expect(romanizeRef("400")).to.be.eql("CD"); + expect(romanizeRef("499")).to.be.eql("CDXCIX"); + expect(romanizeRef("900")).to.be.eql("CM"); + expect(romanizeRef("999")).to.be.eql("CMXCIX"); + expect(romanizeRef("4000")).to.be.eql("MMMM"); + }); + it("preserves suffixes", function () { + expect(romanizeRef("2A")).to.be.eql("IIA"); + expect(romanizeRef("2I")).to.be.eql("III"); + }); + }); +}); diff --git a/shieldlib/tsconfig.json b/shieldlib/tsconfig.json new file mode 100644 index 000000000..3446656ce --- /dev/null +++ b/shieldlib/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2019", + "module": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "exclude": ["node_modules", "**/*.json"], + "include": ["src/**/*.ts", "scripts/**.ts", "src/shield.js", "src/shield.js"] +} diff --git a/src/americana.js b/src/americana.js index b0ed4f57c..e208b7cdf 100644 --- a/src/americana.js +++ b/src/americana.js @@ -3,13 +3,6 @@ import config from "./config.js"; import * as Label from "./constants/label.js"; -import * as Style from "./js/style.js"; - -import * as Shield from "./js/shield.js"; -import * as ShieldDef from "./js/shield_defs.js"; -import * as CustomShields from "./js/custom_shields.js"; - -import * as Poi from "./js/poi.js"; import * as languageLabel from "./js/language_label.js"; @@ -22,16 +15,8 @@ import * as LegendConfig from "./js/legend_config.js"; import SampleControl from "openmapsamples-maplibre/OpenMapSamplesControl.js"; import { default as OpenMapTilesSamples } from "openmapsamples/samples/OpenMapTiles/index.js"; -export function buildStyle() { - var getUrl = window.location; - var baseUrl = getUrl.protocol + "//" + getUrl.host + getUrl.pathname; - return Style.build( - config.OPENMAPTILES_URL, - `${baseUrl}/sprites/sprite`, - `${baseUrl}/fonts/{fontstack}/{range}.pbf`, - Label.getLocales() - ); -} +import { createMap, loadRTLPlugin, buildStyle } from "./js/map_builder.js"; +import { debugOptions } from "./debug_config.js"; function upgradeLegacyHash() { let hash = window.location.hash.substr(1); @@ -42,40 +27,74 @@ function upgradeLegacyHash() { } upgradeLegacyHash(); -maplibregl.setRTLTextPlugin( - "https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js", - null, - true +loadRTLPlugin(); + +export const map = createMap( + window, + (shields) => shieldDefLoad(shields), + { + container: "map", // container id + hash: "map", + antialias: true, + style: buildStyle(), + center: [-94, 40.5], + zoom: 4, + attributionControl: false, + }, + debugOptions ); -window.maplibregl = maplibregl; -export const map = (window.map = new maplibregl.Map({ - container: "map", // container id - hash: "map", - antialias: true, - style: buildStyle(), - center: [-94, 40.5], // starting position [lng, lat] - zoom: 4, // starting zoom - attributionControl: false, -})); - -CustomShields.loadCustomShields(); -ShieldDef.loadShields(); - -map.on("styleimagemissing", function (e) { - switch (e.id.split("\n")[0]) { - case "shield": - Shield.missingIconHandler(map, e); - break; - case "poi": - Poi.missingIconHandler(map, e); - break; - default: - console.warn("Image id not recognized:", JSON.stringify(e.id)); - break; - } +// Add our sample data. +let sampleControl = new SampleControl({ permalinks: true }); +OpenMapTilesSamples.forEach((sample, i) => { + sampleControl.addSample(sample); }); +let legendControl; + +function shieldDefLoad(shields) { + legendControl = new LegendControl(shields); + legendControl.sections = LegendConfig.sections; + map.addControl(legendControl, "bottom-left"); + map.addControl(sampleControl, "bottom-left"); + + if (window.top === window.self) { + // if not embedded in an iframe, autofocus canvas to enable keyboard shortcuts + map.getCanvas().focus(); + } + + map.addControl(new maplibregl.AttributionControl(attributionConfig)); + map.addControl(languageLabel.label, "bottom-right"); + + map.addControl(new search.PhotonSearchControl(), "top-left"); + map.addControl(new maplibregl.NavigationControl(), "top-left"); + + window.addEventListener("languagechange", (event) => { + console.log(`Changed to ${navigator.languages}`); + hotReloadMap(); + updateLanguageLabel(); + }); + + window.addEventListener("hashchange", (event) => { + upgradeLegacyHash(); + let oldLanguage = Label.getLanguageFromURL(new URL(event.oldURL)); + let newLanguage = Label.getLanguageFromURL(new URL(event.newURL)); + if (oldLanguage !== newLanguage) { + console.log(`Changed to ${newLanguage}`); + hotReloadMap(); + updateLanguageLabel(); + } + }); + + updateLanguageLabel(); + + if (window.LIVE_RELOAD) { + new EventSource("/esbuild").addEventListener("change", () => + location.reload() + ); + } +} + function hotReloadMap() { map.setStyle(buildStyle()); } @@ -85,25 +104,6 @@ export function updateLanguageLabel() { legendControl.onLanguageChange(); } -let legendControl = new LegendControl(); -legendControl.sections = LegendConfig.sections; -window.addEventListener("languagechange", (event) => { - console.log(`Changed to ${navigator.languages}`); - hotReloadMap(); - updateLanguageLabel(); -}); - -window.addEventListener("hashchange", (event) => { - upgradeLegacyHash(); - let oldLanguage = Label.getLanguageFromURL(new URL(event.oldURL)); - let newLanguage = Label.getLanguageFromURL(new URL(event.newURL)); - if (oldLanguage !== newLanguage) { - console.log(`Changed to ${newLanguage}`); - hotReloadMap(); - updateLanguageLabel(); - } -}); - let attributionConfig = { customAttribution: "", }; @@ -114,32 +114,7 @@ if (config.ATTRIBUTION_TEXT != undefined) { }; } -map.addControl(new maplibregl.AttributionControl(attributionConfig)); -map.addControl(languageLabel.label, "bottom-right"); - if (config.ATTRIBUTION_LOGO != undefined) { document.getElementById("attribution-logo").innerHTML = config.ATTRIBUTION_LOGO; } - -map.addControl(new search.PhotonSearchControl(), "top-left"); -map.addControl(new maplibregl.NavigationControl(), "top-left"); - -map.addControl(legendControl, "bottom-left"); - -// Add our sample data. -let sampleControl = new SampleControl({ permalinks: true }); -OpenMapTilesSamples.forEach((sample, i) => { - sampleControl.addSample(sample); -}); -map.addControl(sampleControl, "bottom-left"); - -map.getCanvas().focus(); - -updateLanguageLabel(); - -if (window.LIVE_RELOAD) { - new EventSource("/esbuild").addEventListener("change", () => - location.reload() - ); -} diff --git a/src/bare_americana.js b/src/bare_americana.js new file mode 100644 index 000000000..a0bdb3081 --- /dev/null +++ b/src/bare_americana.js @@ -0,0 +1,37 @@ +"use strict"; + +import "maplibre-gl/dist/maplibre-gl.css"; + +import { createMap, loadRTLPlugin, buildStyle } from "./js/map_builder.js"; +import { debugOptions } from "./debug_config.js"; + +loadRTLPlugin(); + +export const map = createMap( + window, + (shields) => shieldDefLoad(), + { + container: "map", // container id + hash: "map", + antialias: true, + style: buildStyle(), + center: [-94, 40.5], + zoom: 4, + fadeDuration: 0, + attributionControl: false, + }, + debugOptions +); + +function shieldDefLoad() { + if (window.top === window.self) { + // if not embedded in an iframe, autofocus canvas to enable keyboard shortcuts + map.getCanvas().focus(); + } + + if (window.LIVE_RELOAD) { + new EventSource("/esbuild").addEventListener("change", () => + location.reload() + ); + } +} diff --git a/src/bare_map.html b/src/bare_map.html new file mode 100644 index 000000000..4be225275 --- /dev/null +++ b/src/bare_map.html @@ -0,0 +1,32 @@ + + + + + OpenStreetMap Americana + + + + + + + + + +
+ + diff --git a/src/configs/config.aws.js b/src/configs/config.aws.js index e92929f29..9727e7f64 100644 --- a/src/configs/config.aws.js +++ b/src/configs/config.aws.js @@ -3,8 +3,7 @@ /* Planetiler tile server, hosted at AWS */ -const OPENMAPTILES_URL = - "https://6ug7hetxl9.execute-api.us-east-2.amazonaws.com/data/v3.json"; +const OPENMAPTILES_URL = "https://tile.ourmap.us/data/v3.json"; /* The following two variables override the color of the bounding box and halo of @@ -14,8 +13,15 @@ Both accept an HTML color name, hex code, or other CSS color value. const SHIELD_TEXT_BBOX_COLOR = null; const SHIELD_TEXT_HALO_COLOR_OVERRIDE = null; +/* +Uncomment the following line here and in the export block to change the location +of the font stack (normally, for development and test) +*/ +//const FONT_URL = font_stack_url + export default { OPENMAPTILES_URL, SHIELD_TEXT_BBOX_COLOR, SHIELD_TEXT_HALO_COLOR_OVERRIDE, + // FONT_URL, }; diff --git a/src/constants/color.js b/src/constants/color.js index 4472ec6b7..e2bf7bdc7 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -40,6 +40,7 @@ export const palette = { pink: "#df4661", // Pantone 198 purple: "#6d2077", // Pantone 259 red: "#bf2033", // Pantone 187 + tan: "#ddcba4", // Pantone 468 C white: "white", yellow: "#ffcd00", // Pantone 116 yellow_green: "#c4d600", // Pantone 382 @@ -54,6 +55,7 @@ export const shields = { pink: palette.pink, // Pantone 198 purple: palette.purple, // Pantone 259 red: palette.red, // Pantone 187 + tan: palette.tan, // Pantone 468 C white: palette.white, yellow: palette.yellow, // Pantone 116 yellow_green: palette.yellow_green, // Pantone 382 diff --git a/src/debug_config.ts b/src/debug_config.ts new file mode 100644 index 000000000..c1285f49d --- /dev/null +++ b/src/debug_config.ts @@ -0,0 +1,14 @@ +import { DebugOptions } from "@americana/maplibre-shield-generator/src/types.js"; +import config from "./config.js"; + +export const debugOptions: DebugOptions = {}; + +if (config.SHIELD_TEXT_HALO_COLOR_OVERRIDE) { + debugOptions.shieldTextHaloColor = config.SHIELD_TEXT_HALO_COLOR_OVERRIDE; +} + +if (config.SHIELD_TEXT_BBOX_COLOR) { + debugOptions.shieldTextBboxColor = config.SHIELD_TEXT_BBOX_COLOR; +} + +export { debugOptions as default }; diff --git a/src/fonts.css b/src/fonts.css new file mode 100644 index 000000000..89471403c --- /dev/null +++ b/src/fonts.css @@ -0,0 +1,20 @@ +@font-face { + font-family: "Noto Sans Condensed"; + src: url("https://osm-americana.github.io/web-fonts/noto/NotoSans-CondensedMedium-PropNums.woff2") + format("woff2"), + url("https://osm-americana.github.io/web-fonts/noto/NotoSans-CondensedMedium-PropNums.woff") + format("woff"); + font-weight: 500; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Noto Sans Armenian Condensed"; + src: url("https://osm-americana.github.io/web-fonts/noto/NotoSansArmenian-CondensedMedium-PropNums.woff2") + format("woff2"), + url("https://osm-americana.github.io/web-fonts/noto/NotoSansArmenian-CondensedMedium-PropNums.woff") + format("woff"); + font-weight: 500; + font-style: normal; + font-display: swap; +} diff --git a/src/index.html b/src/index.html index ef2a27aaa..fff7f541d 100644 --- a/src/index.html +++ b/src/index.html @@ -3,8 +3,8 @@ OpenStreetMap Americana - - + + - @@ -163,31 +159,5 @@

Legend

-

- Invisible text so the font will load early -

-

- Invisible text so the font will load early -

-

- Invisible text so the font will load early -

-

- Invisible text so the font will load early -

diff --git a/src/js/custom_shields.js b/src/js/custom_shields.js deleted file mode 100644 index 63036d6b9..000000000 --- a/src/js/custom_shields.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; - -import * as Color from "../constants/color.js"; -import * as ShieldDraw from "./shield_canvas_draw.js"; - -// Special case for Allegheny, PA Belt System, documented in CONTRIBUTE.md -export function paBelt(ctx, params) { - ShieldDraw.roundedRectangle(ctx, { - fillColor: Color.shields.white, - strokeColor: Color.shields.black, - outlineWidth: 1, - radius: 2, - rectWidth: 20, - }); - - let fillColor = params.fillColor; - - let lineWidth = 0.5 * ShieldDraw.PXR; - let diameter = ShieldDraw.CS / 3 - lineWidth; - ctx.beginPath(); - ctx.arc( - ShieldDraw.CS / 2, - ShieldDraw.CS / 2, - diameter, - 0, - 2 * Math.PI, - false - ); - - ctx.fillStyle = fillColor; - ctx.strokeStyle = params.strokeColor; - ctx.fill(); - - ctx.lineWidth = lineWidth; - ctx.stroke(); - return ctx; -} - -// Special case for Branson color-coded routes, documented in CONTRIBUTE.md -export function bransonRoute(ctx, params) { - ShieldDraw.roundedRectangle(ctx, { - fillColor: Color.shields.green, - strokeColor: Color.shields.white, - outlineWidth: 1, - radius: 2, - rectWidth: 20, - }); - - let fillColor = params.fillColor; - - let lineWidth = 0.5 * ShieldDraw.PXR; - let x = 0.15 * ShieldDraw.CS + lineWidth; - let width = 0.7 * ShieldDraw.CS - 2 * lineWidth; - - let y = 0.4 * ShieldDraw.CS + lineWidth; - let height = 0.45 * ShieldDraw.CS - 2 * lineWidth; - - ctx.beginPath(); - ctx.rect(x, y, width, height); - - ctx.fillStyle = fillColor; - ctx.strokeStyle = params.strokeColor; - ctx.fill(); - - ctx.lineWidth = lineWidth; - ctx.stroke(); - return ctx; -} - -export function loadCustomShields() { - ShieldDraw.registerDrawFunction("branson", bransonRoute, 20); - ShieldDraw.registerDrawFunction("paBelt", paBelt, 20); -} diff --git a/src/js/legend_control.js b/src/js/legend_control.js index 0a9d8e8e0..3ff1905a1 100644 --- a/src/js/legend_control.js +++ b/src/js/legend_control.js @@ -1,15 +1,14 @@ "use strict"; -import * as ShieldDraw from "./shield_canvas_draw.js"; +import { getDOMPixelRatio } from "@americana/maplibre-shield-generator"; import * as Label from "../constants/label.js"; -import * as ShieldDef from "./shield_defs.js"; import * as HighwayShieldLayers from "../layer/highway_shield.js"; import * as maplibregl from "maplibre-gl"; const maxPopupWidth = 30; /* em */ - +const PXR = getDOMPixelRatio(); /** * Wikidata labels are normally lowercased so that they can appear in any * context. Convert them to sentence case for consistency with the rest of the @@ -20,6 +19,10 @@ function toSentenceCase(lowerCase, locale) { } export default class LegendControl { + constructor(shieldDefs) { + this._shieldDefs = shieldDefs; + } + onAdd(map) { this._map = map; @@ -392,10 +395,7 @@ export default class LegendControl { let width = f.layer.paint["line-width"] ?? 1; let gapWidth = f.layer.paint["line-gap-width"]; // Round the stroke width up to one point to ensure legibility. - return Math.max( - 1 / ShieldDraw.PXR, - gapWidth ? width * 2 + gapWidth : width - ); + return Math.max(1 / PXR, gapWidth ? width * 2 + gapWidth : width); }; let lineWidths = lineFeatures.map(getLineWidth); let height = Math.max(...lineWidths); @@ -470,7 +470,7 @@ export default class LegendControl { } let networkImages = imagesByNetwork[image.network]; - let shieldDef = ShieldDef.shields[image.network]; + let shieldDef = this._shieldDefs[image.network]; if (image.ref && shieldDef?.overrideByRef?.[image.ref]) { // Store a different image for each override in the shield definition. if (!networkImages.overridesByRef[image.ref]) { @@ -508,7 +508,7 @@ export default class LegendControl { // order as in the shield definitions, appending all the unrecognized // networks sorted in alphabetical order. let networks = [ - ...Object.keys(ShieldDef.shields), + ...Object.keys(this._shieldDefs), ...[...unrecognizedNetworks.values()].sort(), ]; let countries = new Set(); @@ -686,8 +686,8 @@ export default class LegendControl { // Embed the canvas in an HTML image of the same size. let img = new Image( - (imageData.width * iconSize) / ShieldDraw.PXR, - (imageData.height * iconSize) / ShieldDraw.PXR + (imageData.width * iconSize) / PXR, + (imageData.height * iconSize) / PXR ); img.src = canvas.toDataURL("image/png"); img.className = "shield"; diff --git a/src/js/map_builder.ts b/src/js/map_builder.ts new file mode 100644 index 000000000..3577f10d5 --- /dev/null +++ b/src/js/map_builder.ts @@ -0,0 +1,86 @@ +/** + * Functions for assembling user-facing map components + */ +import { + ShieldDefinitions, + URLShieldRenderer, +} from "@americana/maplibre-shield-generator"; +import config from "../config.js"; + +import { + shieldPredicate, + networkPredicate, + routeParser, +} from "../js/shield_format.js"; + +import * as Poi from "../js/poi.js"; +import * as Label from "../constants/label.js"; +import * as Style from "../js/style.js"; +import maplibregl, { Map, MapOptions, StyleSpecification } from "maplibre-gl"; +import { DebugOptions } from "@americana/maplibre-shield-generator/src/types.js"; + +export function buildStyle(): StyleSpecification { + var getUrl = window.location; + var baseUrl = ( + getUrl.protocol + + "//" + + getUrl.host + + removeAfterLastSlash(getUrl.pathname) + ) + //Trim trailing slashes from URL + .replace(/\/+$/, ""); + return Style.build( + config.OPENMAPTILES_URL, + `${baseUrl}/sprites/sprite`, + config.FONT_URL ?? + "https://osm-americana.github.io/fontstack66/{fontstack}/{range}.pbf", + Label.getLocales() + ); +} + +function removeAfterLastSlash(str: string): string { + const lastSlashIndex = str.lastIndexOf("/"); + if (lastSlashIndex === -1) { + return str; // return the original string if no slash is found + } + return str.substring(0, lastSlashIndex + 1); +} + +export function loadRTLPlugin(): void { + maplibregl.setRTLTextPlugin( + "https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js", + null, + true + ); +} + +export function createMap( + window: Window, + shieldDefCallback: (shields: ShieldDefinitions) => void, + options: MapOptions, + debugOptions: DebugOptions +): Map { + window["maplibregl"] = maplibregl; + let map: Map = (window["map"] = new maplibregl.Map(options)); + + const shieldRenderer = new URLShieldRenderer("shields.json", routeParser) + .debugOptions(debugOptions) + .filterImageID(shieldPredicate) + .filterNetwork(networkPredicate) + .renderOnMaplibreGL(map) + .onShieldDefLoad(shieldDefCallback); + + map.on("styleimagemissing", function (e) { + switch (e.id.split("\n")[0]) { + case "shield": + break; + case "poi": + Poi.missingIconHandler(shieldRenderer, map, e); + break; + default: + console.warn("Image id not recognized:", JSON.stringify(e.id)); + break; + } + }); + return map; +} diff --git a/src/js/poi.js b/src/js/poi.js deleted file mode 100644 index 3f8c2d786..000000000 --- a/src/js/poi.js +++ /dev/null @@ -1,42 +0,0 @@ -import * as Gfx from "./screen_gfx.js"; - -export const PXR = Gfx.getPixelRatio(); - -export function missingIconHandler(map, e) { - try { - missingIconLoader(map, e); - } catch (err) { - console.error(`Exception while loading image ‘${e?.id}’:\n`, err); - } -} - -export function missingIconLoader(map, e) { - var sprite = e.id.split("\n")[1].split("=")[1]; - var color = e.id.split("\n")[2].split("=")[1]; - - var sourceSprite = map.style.imageManager.images[sprite]; - var width = sourceSprite.data.width; - var height = sourceSprite.data.height; - - var ctx = Gfx.getGfxContext({ width, height }); - Gfx.copyImageData(ctx, sourceSprite, 0, false, color); - - if (ctx == null) { - // Want to return null here, but that gives a corrupted display. See #243 - console.warn("Didn't produce an icon for", JSON.stringify(e.id)); - ctx = Gfx.getGfxContext({ width: 1, height: 1 }); - } - - const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - map.addImage( - e.id, - { - width: ctx.canvas.width, - height: ctx.canvas.height, - data: imgData.data, - }, - { - pixelRatio: PXR, - } - ); -} diff --git a/src/js/poi.ts b/src/js/poi.ts new file mode 100644 index 000000000..8f6da6688 --- /dev/null +++ b/src/js/poi.ts @@ -0,0 +1,66 @@ +import { + transposeImageData, + AbstractShieldRenderer, +} from "@americana/maplibre-shield-generator"; +import { Map, MapStyleImageMissingEvent, StyleImage } from "maplibre-gl"; + +export function missingIconHandler( + shieldRenderer: AbstractShieldRenderer, + map: Map, + e: MapStyleImageMissingEvent +) { + try { + missingIconLoader(shieldRenderer, map, e); + } catch (err) { + console.error(`Exception while loading image ‘${e?.id}’:\n`, err); + } +} + +export function missingIconLoader( + shieldRenderer: AbstractShieldRenderer, + map: Map, + e: MapStyleImageMissingEvent +) { + const sprite: string = e.id.split("\n")[1].split("=")[1]; + const color: string = e.id.split("\n")[2].split("=")[1]; + + const sourceSprite: StyleImage = map.style.getImage(sprite); + + if (!sourceSprite) { + console.error(`No such sprite ${sprite}`); + return; + } + + const width: number = sourceSprite.data.width; + const height: number = sourceSprite.data.height; + + let ctx: CanvasRenderingContext2D = shieldRenderer.createGraphics({ + width, + height, + }); + transposeImageData(ctx, sourceSprite, 0, false, color); + + if (ctx == null) { + // Want to return null here, but that gives a corrupted display. See #243 + console.warn("Didn't produce an icon for", JSON.stringify(e.id)); + ctx = shieldRenderer.createGraphics({ width: 1, height: 1 }); + } + + const imgData: ImageData = ctx.getImageData( + 0, + 0, + ctx.canvas.width, + ctx.canvas.height + ); + map.addImage( + e.id, + { + width: ctx.canvas.width, + height: ctx.canvas.height, + data: imgData.data, + }, + { + pixelRatio: shieldRenderer.pixelRatio(), + } + ); +} diff --git a/src/js/screen_gfx.js b/src/js/screen_gfx.js deleted file mode 100644 index 58e38157a..000000000 --- a/src/js/screen_gfx.js +++ /dev/null @@ -1,137 +0,0 @@ -"use strict"; - -import rgba from "color-rgba"; - -const nonLatinFonts = - '"Noto Naskh Arabic", "Noto Sans Armenian", "Noto Sans Georgian"'; -const fontFamily = `"sans-serif-condensed", "Arial Narrow", ${nonLatinFonts}, sans-serif`; -export const shieldFont = (size) => `bold ${size}px ${fontFamily}`; -export const fontSizeThreshold = 12; - -var gfxFactory = getDocumentGfxContext; - -export function setGfxFactory(factory) { - gfxFactory = factory; -} - -export function getGfxContext(bounds) { - return gfxFactory(bounds); -} - -export function getDocumentGfxContext(bounds) { - var ctx = document.createElement("canvas").getContext("2d"); - var ctx = document - .createElement("canvas") - .getContext("2d", { willReadFrequently: true }); - ctx.imageSmoothingQuality = "high"; - ctx.textAlign = "center"; - ctx.textBaseline = "top"; - ctx.canvas.width = bounds.width; - ctx.canvas.height = bounds.height; - return ctx; -} - -/** - * Get pixel ratio MapLibre is using for icons based on devicePixelRatio (DPR). - * MapLibre uses 2x sprites whenever DPR > 1 and 1x sprites otherwise. - * So this function will always return 1 or 2 as appropriate based on DPR. - */ -export function getPixelRatio() { - return (typeof window !== "undefined" && window.devicePixelRatio) > 1 ? 2 : 1; -} - -// Replaces `sourceVal` with a blend of `lightenVal` and `darkenVal` proportional to the brightness; -// i.e. white becomes `darkenVal`, black becomes `lightenVal`, and anit-aliased pixels remain anit-aliased -export function blendedColorVal(sourceVal, lightenVal, darkenVal) { - return ( - 255 - - (sourceVal / 255) * (255 - darkenVal) - - (1 - sourceVal / 255) * (255 - lightenVal) - ); -} - -export function copyPixel(source, dest, sourceOffset, destOffset) { - //Red - dest[destOffset] = source[sourceOffset]; - //Green - dest[destOffset + 1] = source[sourceOffset + 1]; - //Blue - dest[destOffset + 2] = source[sourceOffset + 2]; - //Alpha - dest[destOffset + 3] = source[sourceOffset + 3]; -} - -export function copyAndRecolorPixel( - source, - dest, - sourceOffset, - destOffset, - colorLighten, - colorDarken -) { - //Red - dest[destOffset] = blendedColorVal( - source[sourceOffset], - colorLighten[0], - colorDarken[0] - ); - //Green - dest[destOffset + 1] = blendedColorVal( - source[sourceOffset + 1], - colorLighten[1], - colorDarken[1] - ); - //Blue - dest[destOffset + 2] = blendedColorVal( - source[sourceOffset + 2], - colorLighten[2], - colorDarken[2] - ); - //Alpha - dest[destOffset + 3] = source[sourceOffset + 3]; -} - -export function copyImageData( - ctx, - sourceImage, - yOffset, - verticalReflect, - colorLighten, - colorDarken -) { - let imgData = ctx.createImageData( - sourceImage.data.width, - sourceImage.data.height - ); - - let pixelCopyFn = - colorLighten || colorDarken ? copyAndRecolorPixel : copyPixel; - - let lighten = colorLighten ? rgba(colorLighten) : rgba("#000"); - let darken = colorDarken ? rgba(colorDarken) : rgba("#fff"); - - if (!verticalReflect) { - for (let i = 0; i < sourceImage.data.data.length; i += 4) { - pixelCopyFn(sourceImage.data.data, imgData.data, i, i, lighten, darken); - } - } else { - //4 bytes/px, copy in reverse vertical order. - for (let y = 0; y < sourceImage.data.height; y++) { - for (let x = 0; x < sourceImage.data.width; x++) { - let destRow = sourceImage.data.height - y - 1; - let destIdx = (destRow * sourceImage.data.width + x) * 4; - let srcIdx = (y * sourceImage.data.width + x) * 4; - pixelCopyFn( - sourceImage.data.data, - imgData.data, - srcIdx, - destIdx, - lighten, - darken - ); - } - } - } - - ctx.putImageData(imgData, 0, yOffset); -} diff --git a/src/js/shield.js b/src/js/shield.js deleted file mode 100644 index ebc364715..000000000 --- a/src/js/shield.js +++ /dev/null @@ -1,426 +0,0 @@ -"use strict"; - -import config from "../config.js"; - -import * as ShieldDef from "./shield_defs.js"; -import * as ShieldText from "./shield_text.js"; -import * as ShieldDraw from "./shield_canvas_draw.js"; -import * as Gfx from "./screen_gfx.js"; - -function drawBannerPart(ctx, network, drawFunc) { - var shieldDef = ShieldDef.shields[network]; - - if (shieldDef == null || typeof shieldDef.modifiers == "undefined") { - return ctx; //Unadorned shield - } - - for (var i = 0; i < shieldDef.modifiers.length; i++) { - drawFunc(ctx, shieldDef.modifiers[i], i); - } - - return ctx; -} - -function compoundShieldSize(dimension, bannerCount) { - return { - width: dimension.width, - height: dimension.height + bannerCount * ShieldDef.bannerSizeH, - }; -} - -export function isValidNetwork(network) { - // On recreational route relations, network=* indicates the network's scope, - // not the network itself. - // https://github.com/ZeLonewolf/openstreetmap-americana/issues/94 - return !/^[lrni][chimpw]n$/.test(network); -} - -export function isValidRef(ref) { - return ref !== null && ref.length !== 0 && ref.length <= 6; -} - -/** - * Get the number of banner placards associated with this shield - * - * @param {*} shield - Shield definition - * @returns the number of banner placards that need to be drawn - */ -export function getBannerCount(shield) { - if (shield == null || typeof shield.modifiers == "undefined") { - return 0; //Unadorned shield - } - return shield.modifiers.length; -} - -/** - * Retrieve the shield blank that goes with a particular route. If there are - * multiple shields for a route (different widths), it picks the best shield. - * - * @param {*} map - maplibre Map - * @param {*} shieldDef - shield definition for this route - * @param {*} routeDef - route tagging from OSM - * @returns shield blank or null if no shield exists - */ -function getRasterShieldBlank(map, shieldDef, routeDef) { - var shieldArtwork = null; - var textLayout; - var bannerCount = 0; - var bounds; - - if (Array.isArray(shieldDef.spriteBlank)) { - for (var i = 0; i < shieldDef.spriteBlank.length; i++) { - shieldArtwork = map.style.getImage(shieldDef.spriteBlank[i]); - - bounds = compoundShieldSize(shieldArtwork.data, bannerCount); - textLayout = ShieldText.layoutShieldTextFromDef( - routeDef.ref, - shieldDef, - bounds - ); - if (textLayout.fontPx > Gfx.fontSizeThreshold * Gfx.getPixelRatio()) { - break; - } - } - } else { - shieldArtwork = map.style.getImage(shieldDef.spriteBlank); - } - - return shieldArtwork; -} - -function textColor(shieldDef) { - if (shieldDef != null && typeof shieldDef.textColor != "undefined") { - return shieldDef.textColor; - } - return "black"; -} - -function getDrawFunc(shieldDef) { - if (typeof shieldDef.canvasDrawnBlank != "undefined") { - return (ctx, ref) => - ShieldDraw.draw( - shieldDef.canvasDrawnBlank.drawFunc, - ctx, - shieldDef.canvasDrawnBlank.params, - ref - ); - } - return ShieldDraw.blank; -} - -export function drawShield(ctx, shieldDef, routeDef) { - let bannerCount = getBannerCount(shieldDef); - let yOffset = bannerCount * ShieldDef.bannerSizeH; - - //Shift canvas to draw shield below banner - ctx.save(); - ctx.translate(0, yOffset); - let drawFunc = getDrawFunc(shieldDef); - drawFunc(ctx, routeDef.ref); - ctx.restore(); -} - -function getDrawHeight(shieldDef) { - if (typeof shieldDef.canvasDrawnBlank != "undefined") { - return ShieldDraw.shapeHeight(shieldDef.canvasDrawnBlank.drawFunc); - } - return ShieldDraw.CS; -} - -function drawShieldText(ctx, map, shieldDef, routeDef) { - var bannerCount = getBannerCount(shieldDef); - var shieldBounds = null; - - var shieldArtwork = getRasterShieldBlank(map, shieldDef, routeDef); - let yOffset = bannerCount * ShieldDef.bannerSizeH; - - if (shieldArtwork == null) { - ctx.translate(0, yOffset); - let drawFunc = getDrawFunc(shieldDef); - drawFunc(ctx, routeDef.ref); - ctx.translate(0, -yOffset); - - shieldBounds = { - width: ctx.canvas.width, - height: getDrawHeight(shieldDef), - }; - } else { - shieldBounds = { - width: shieldArtwork.data.width, - height: shieldArtwork.data.height, - }; - } - - if (shieldDef.notext) { - //If the shield definition says not to draw a ref, ignore ref - return ctx; - } - - //The ref is valid and we're supposed to draw it - var textLayout = ShieldText.layoutShieldTextFromDef( - routeDef.ref, - shieldDef, - shieldBounds - ); - - textLayout.yBaseline += bannerCount * ShieldDef.bannerSizeH; - - if (config.SHIELD_TEXT_HALO_COLOR_OVERRIDE) { - ctx.strokeStyle = config.SHIELD_TEXT_HALO_COLOR_OVERRIDE; - ShieldText.drawShieldHaloText(ctx, routeDef.ref, textLayout); - } else if (shieldDef.textHaloColor) { - ctx.strokeStyle = shieldDef.textHaloColor; - ShieldText.drawShieldHaloText(ctx, routeDef.ref, textLayout); - } - - ctx.fillStyle = textColor(shieldDef); - ShieldText.drawShieldText(ctx, routeDef.ref, textLayout); - - if (config.SHIELD_TEXT_BBOX_COLOR) { - ctx.strokeStyle = config.SHIELD_TEXT_BBOX_COLOR; - ctx.lineWidth = ShieldDraw.PXR; - ctx.strokeRect( - (shieldDef.padding.left - 0.5) * ShieldDraw.PXR, - bannerCount * ShieldDef.bannerSizeH + - (shieldDef.padding.top - 0.5) * ShieldDraw.PXR, - shieldBounds.width - - (shieldDef.padding.left + shieldDef.padding.right - 1) * ShieldDraw.PXR, - shieldBounds.height - - (shieldDef.padding.top + shieldDef.padding.bottom - 1) * ShieldDraw.PXR - ); - } - - return ctx; -} - -export function missingIconHandler(map, e) { - try { - missingIconLoader(map, e); - } catch (err) { - console.error(`Exception while loading image ‘${e?.id}’:\n`, err); - } -} - -export function missingIconLoader(map, e) { - let ctx = generateShieldCtx(map, e.id); - if (ctx == null) { - // Want to return null here, but that gives a corrupted display. See #243 - console.warn("Didn't produce a shield for", JSON.stringify(e.id)); - ctx = Gfx.getGfxContext({ width: 1, height: 1 }); - } - const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - map.addImage( - e.id, - { - width: ctx.canvas.width, - height: ctx.canvas.height, - data: imgData.data, - }, - { - pixelRatio: ShieldDraw.PXR, - } - ); -} - -function refForDefs(routeDef, shieldDef) { - // Handle special case for manually-applied abbreviations - if ( - shieldDef.refsByWayName && - routeDef.wayName && - shieldDef.refsByWayName[routeDef.wayName] - ) { - return shieldDef.refsByWayName[routeDef.wayName]; - } - return routeDef.ref; -} - -export function getShieldDef(routeDef) { - if (routeDef == null) { - return null; - } - - var shieldDef = ShieldDef.shields[routeDef.network]; - - if (shieldDef == null) { - // Default to plain black text with halo and no background shield - console.debug("Generic shield for", JSON.stringify(routeDef)); - return isValidNetwork(routeDef.network) && isValidRef(routeDef.ref) - ? ShieldDef.shields.default - : null; - } - - var ref = refForDefs(routeDef, shieldDef); - - if (shieldDef.overrideByRef) { - shieldDef = { - ...shieldDef, - ...shieldDef.overrideByRef[ref], - }; - } - - if (shieldDef.overrideByWayName) { - shieldDef = { - ...shieldDef, - ...shieldDef.overrideByWayName[routeDef.wayName || ""], - }; - } - - //Special case where there's a defined fallback shield when no ref is tagged - //Example: PA Turnpike - if (!isValidRef(ref) && "noref" in shieldDef) { - shieldDef = shieldDef.noref; - // noref implies notext - shieldDef.notext = true; - } - - //Determine whether a route without a ref gets drawn - if ( - !isValidRef(ref) && - !shieldDef.notext && - !(shieldDef.refsByWayName && routeDef.wayName) - ) { - return null; - } - - return shieldDef; -} - -export function getRouteDef(id) { - if (id == "shield_") { - return null; - } - - var network_ref = id.split("\n")[1]; - var network_ref_parts = network_ref.split("="); - var network = network_ref_parts[0]; - var ref = network_ref_parts[1]; - var wayName = id.split("\n")[2]; - - return { - network: network, - ref: ref, - wayName: wayName, - }; -} - -/** - * Reformats an alphanumeric ref as Eastern Arabic numerals, preserving any - * alphabetic suffix. - */ -export const arabizeRef = (ref) => - ref.replaceAll(/[0-9]/g, (m) => String.fromCharCode(0x0660 + parseInt(m))); - -/** - * Reformats an alphanumeric ref as Roman numerals, preserving any alphabetic - * suffix. - */ -export function romanizeRef(ref) { - let number = parseInt(ref, 10); - if (isNaN(number)) { - return ref; - } - - let roman = - "M".repeat(number / 1000) + - "D".repeat((number % 1000) / 500) + - "C".repeat((number % 500) / 100) + - "L".repeat((number % 100) / 50) + - "X".repeat((number % 50) / 10) + - "V".repeat((number % 10) / 5) + - "I".repeat(number % 5); - roman = roman - .replace("DCCCC", "CM") - .replace("CCCC", "CD") - .replace("LXXXX", "XC") - .replace("XXXX", "XL") - .replace("VIIII", "IX") - .replace("IIII", "IV"); - return roman + ref.slice(number.toString().length); -} - -export function getDrawnShieldBounds(shieldDef, ref) { - let width = Math.max( - ShieldDraw.CS, - ShieldDraw.computeWidth( - shieldDef.canvasDrawnBlank.params, - ref, - shieldDef.canvasDrawnBlank.drawFunc - ) - ); - let height = ShieldDraw.shapeHeight(shieldDef.canvasDrawnBlank.drawFunc); - return { width, height }; -} - -export function generateShieldCtx(map, id) { - let routeDef = getRouteDef(id); - let shieldDef = getShieldDef(routeDef); - - if (shieldDef == null) { - return null; - } - - routeDef.ref = refForDefs(routeDef, shieldDef); - - //Determine overall shield+banner dimensions - let bannerCount = getBannerCount(shieldDef); - - let sourceSprite = getRasterShieldBlank(map, shieldDef, routeDef); - - let width = ShieldDraw.CS; - let height = ShieldDraw.CS; - - if (sourceSprite == null) { - if (typeof shieldDef.canvasDrawnBlank != "undefined") { - let bounds = getDrawnShieldBounds(shieldDef, routeDef.ref); - width = bounds.width; - height = bounds.height; - } - } else { - width = sourceSprite.data.width; - height = sourceSprite.data.height; - } - - let bannerHeight = bannerCount * ShieldDef.bannerSizeH; - height += bannerHeight; - - //Generate empty canvas sized to the graphic - let ctx = Gfx.getGfxContext({ width, height }); - - // Convert numbering systems. Normally alternative numbering systems should be - // tagged directly in ref=*, but some shields use different numbering systems - // for aesthetic reasons only. - if (routeDef.ref) { - switch (shieldDef.numberingSystem) { - case "arab": - routeDef.ref = arabizeRef(routeDef.ref); - break; - case "roman": - routeDef.ref = romanizeRef(routeDef.ref); - break; - } - } - - // Add the halo around modifier plaque text - drawBannerPart(ctx, routeDef.network, ShieldText.drawBannerHaloText); - - if (sourceSprite == null) { - drawShield(ctx, shieldDef, routeDef); - } else { - Gfx.copyImageData( - ctx, - sourceSprite, - bannerHeight, - shieldDef.verticalReflect, - shieldDef.colorLighten, - shieldDef.colorDarken - ); - } - - // Draw the shield text - drawShieldText(ctx, map, shieldDef, routeDef); - - // Add modifier plaque text - drawBannerPart(ctx, routeDef.network, ShieldText.drawBannerText); - - return ctx; -} diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 7bb3fd154..e4e027d7b 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -1,726 +1,32 @@ "use strict"; import * as Color from "../constants/color.js"; -import * as Gfx from "./screen_gfx.js"; - -//Height of modifier banners -export const bannerSizeH = 9 * Gfx.getPixelRatio(); -export const bannerPadding = 0.5 * Gfx.getPixelRatio(); - -export const shields = {}; - -/** - * Draws a shield with an ellipse background - * - * @param {*} fillColor - Color of ellipse background fill - * @param {*} strokeColor - Color of ellipse outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of ellipse (defaults to variable-width) - * @returns a shield definition object - */ -function ovalShield(fillColor, strokeColor, textColor, rectWidth) { - textColor = textColor ?? strokeColor; - return { - canvasDrawnBlank: { - drawFunc: "ellipse", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - }, - }, - textLayout: textConstraint("ellipse"), - padding: { - left: 2, - right: 2, - top: 2, - bottom: 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with circle background (special case of ovalShield) - * - * @param {*} fillColor - Color of circle background fill - * @param {*} strokeColor - Color of circle outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @returns a shield definition object - */ -function circleShield(fillColor, strokeColor, textColor) { - return ovalShield(fillColor, strokeColor, textColor, 20); -} - -function roundedRectTextConstraint(radius) { - return { - constraintFunc: "roundedRect", - options: { - radius: radius, - }, - }; -} - -function textConstraint(fxn) { - return { - constraintFunc: fxn, - }; -} - -/** - * Draws a shield with a rectangle background - * - * @param {*} fillColor - Color of rectangle background fill - * @param {*} strokeColor - Color of rectangle outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of rectangle (defaults to variable-width) - * @param {*} radius - Corner radius of rectangle (defaults to 2) - * @returns a shield definition object - */ -function roundedRectShield( - fillColor, - strokeColor, - textColor, - rectWidth, - radius -) { - textColor = textColor ?? strokeColor; - radius = radius ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "roundedRectangle", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - radius: radius, - }, - }, - textLayout: roundedRectTextConstraint(radius), - padding: { - left: 3, - right: 3, - top: 3, - bottom: 3, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with an escutcheon background, pointed downward - * - * @param {*} offset - Height of curved portion - * @param {*} fillColor - Color of escutcheon background fill - * @param {*} strokeColor - Color of escutcheon outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of escutcheon (defaults to 0) - * @param {*} rectWidth - Width of escutcheon (defaults to variable-width) - * @returns a shield definition object - */ -function escutcheonDownShield( - offset, - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - textColor = textColor ?? strokeColor; - radius = radius ?? 0; - return { - canvasDrawnBlank: { - drawFunc: "escutcheon", - params: { - offset: offset, - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - radius: radius, - outlineWidth: 1, - }, - }, - textLayout: roundedRectTextConstraint(radius), - padding: { - left: 2, - right: 2, - top: 2, - bottom: 0 + offset / 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a fishhead background, pointed downward - * - * @param {*} fillColor - Color of fishhead background fill - * @param {*} strokeColor - Color of fishhead outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of fishhead (defaults to variable-width) - * @returns a shield definition object - */ -function fishheadDownShield(fillColor, strokeColor, textColor, rectWidth) { - textColor = textColor ?? strokeColor; - return { - canvasDrawnBlank: { - drawFunc: "fishhead", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - outlineWidth: 1, - }, - }, - textLayout: textConstraint("roundedRect"), - padding: { - left: 3, - right: 3, - top: 2, - bottom: 6, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a triangle background, pointed downward - * - * @param {*} fillColor - Color of triangle background fill - * @param {*} strokeColor - Color of triangle outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of triangle (defaults to 2) - * @param {*} rectWidth - Width of triangle (defaults to variable-width) - * @returns a shield definition object - */ -function triangleDownShield( - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - textColor = textColor ?? strokeColor; - radius = radius ?? 2; - - return { - canvasDrawnBlank: { - drawFunc: "triangle", - params: { - pointUp: false, - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - radius: radius, - }, - }, - textLayout: textConstraint("triangleDown"), - padding: { - left: 1, - right: 1, - top: 2, - bottom: 1, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a trapezoid background, with the short side on bottom - * - * @param {*} angle - Angle (in degrees) at which sides deviate from vertical - * @param {*} fillColor - Color of trapezoid background fill - * @param {*} strokeColor - Color of trapezoid outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of trapezoid (defaults to 0) - * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) - * @returns a shield definition object - */ -function trapezoidDownShield( - angle, - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - let angleInRadians = (angle * Math.PI) / 180; - textColor = textColor ?? strokeColor; - radius = radius ?? 0; - - return { - canvasDrawnBlank: { - drawFunc: "trapezoid", - params: { - angle: angleInRadians, - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - radius: radius, - }, - }, - textLayout: roundedRectTextConstraint(radius), - padding: { - left: 2 + 10 * Math.tan(angleInRadians), - right: 2 + 10 * Math.tan(angleInRadians), - top: 2, - bottom: 4, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a trapezoid background, with the short side on top - * - * @param {*} angle - Angle (in degrees) at which sides deviate from vertical - * @param {*} fillColor - Color of trapezoid background fill - * @param {*} strokeColor - Color of trapezoid outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of trapezoid (defaults to 0) - * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) - * @returns a shield definition object - */ -function trapezoidUpShield( - angle, - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - let angleInRadians = (angle * Math.PI) / 180; - textColor = textColor ?? strokeColor; - radius = radius ?? 0; - return { - canvasDrawnBlank: { - drawFunc: "trapezoid", - params: { - shortSideUp: true, - angle: angleInRadians, - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - radius: radius, - }, - }, - textLayout: roundedRectTextConstraint(radius), - padding: { - left: 2 + 10 * Math.tan(angleInRadians), - right: 2 + 10 * Math.tan(angleInRadians), - top: 4, - bottom: 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a diamond background - * - * @param {*} fillColor - Color of diamond background fill - * @param {*} strokeColor - Color of diamond outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of diamond (defaults to 2) - * @param {*} rectWidth - Width of diamond (defaults to variable-width) - * @returns a shield definition object - */ -function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { - textColor = textColor ?? strokeColor; - radius = radius ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "diamond", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - radius: radius, - rectWidth: rectWidth, - }, - }, - textLayout: textConstraint("diamond"), - padding: { - left: 1, - right: 1, - top: 1, - bottom: 1, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a pentagon background, pointed upward - * - * @param {*} offset - Height of diagonal edges - * @param {*} angle - Angle (in degrees) at which sides deviate from vertical - * @param {*} fillColor - Color of pentagon background fill - * @param {*} strokeColor - Color of pentagon outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius1 - Corner radius of pointed side of pentagon (defaults to 2) - * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 0) - * @param {*} rectWidth - Width of pentagon (defaults to variable-width) - * @returns a shield definition object - */ -function pentagonUpShield( - offset, - angle, - fillColor, - strokeColor, - textColor, - radius1, - radius2, - rectWidth -) { - let angleInRadians = (angle * Math.PI) / 180; - textColor = textColor ?? strokeColor; - radius1 = radius1 ?? 2; - radius2 = radius2 ?? 0; - return { - canvasDrawnBlank: { - drawFunc: "pentagon", - params: { - offset: offset, - angle: angleInRadians, - fillColor: fillColor, - strokeColor: strokeColor, - radius1: radius1, - radius2: radius2, - rectWidth: rectWidth, - }, - }, - textLayout: { - constraintFunc: "rect", - }, - padding: { - left: 2 + ((20 - offset) * Math.tan(angleInRadians)) / 2, - right: 2 + ((20 - offset) * Math.tan(angleInRadians)) / 2, - top: 1 + offset / 2, - bottom: 3, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a home plate background, pointed downward - * - * @param {*} offset - Height of diagonal edges - * @param {*} fillColor - Color of home plate background fill - * @param {*} strokeColor - Color of home plate outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) - * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) - * @param {*} rectWidth - Width of home plate (defaults to variable-width) - * @returns a shield definition object - */ -function homePlateDownShield( - offset, - fillColor, - strokeColor, - textColor, - radius1, - radius2, - rectWidth -) { - textColor = textColor ?? strokeColor; - radius1 = radius1 ?? 2; - radius2 = radius2 ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "pentagon", - params: { - pointUp: false, - offset: offset, - angle: 0, - fillColor: fillColor, - strokeColor: strokeColor, - radius1: radius1, - radius2: radius2, - rectWidth: rectWidth, - }, - }, - textLayout: roundedRectTextConstraint(radius2), - padding: { - left: 2, - right: 2, - top: 2, - bottom: 1 + offset, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a home plate background, pointed upward - * - * @param {*} offset - Height of diagonal edges - * @param {*} fillColor - Color of home plate background fill - * @param {*} strokeColor - Color of home plate outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) - * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) - * @param {*} rectWidth - Width of home plate (defaults to variable-width) - * @returns a shield definition object - */ -function homePlateUpShield( - offset, - fillColor, - strokeColor, - textColor, - radius1, - radius2, - rectWidth -) { - textColor = textColor ?? strokeColor; - radius1 = radius1 ?? 2; - radius2 = radius2 ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "pentagon", - params: { - pointUp: true, - offset: offset, - angle: 0, - fillColor: fillColor, - strokeColor: strokeColor, - radius1: radius1, - radius2: radius2, - rectWidth: rectWidth, - }, - }, - textLayout: roundedRectTextConstraint(radius2), - padding: { - left: 2, - right: 2, - top: 1 + offset, - bottom: 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a vertically-aligned hexagon background - * - * @param {*} offset - Height of diagonal edges - * @param {*} fillColor - Color of hexagon background fill - * @param {*} strokeColor - Color of hexagon outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of hexagon (defaults to 2) - * @param {*} rectWidth - Width of hexagon (defaults to variable-width) - * @returns a shield definition object - */ -function hexagonVerticalShield( - offset, - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - textColor = textColor ?? strokeColor; - radius = radius ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "hexagonVertical", - params: { - offset: offset, - fillColor: fillColor, - strokeColor: strokeColor, - radius: radius, - rectWidth: rectWidth, - }, - }, - textLayout: roundedRectTextConstraint(radius), - padding: { - left: 2, - right: 2, - top: 1 + offset, - bottom: 1 + offset, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a horizontally-aligned hexagon background - * - * @param {*} angle - Angle (in degrees) at which sides deviate from vertical - * @param {*} fillColor - Color of hexagon background fill - * @param {*} strokeColor - Color of hexagon outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of hexagon (defaults to 2) - * @param {*} rectWidth - Width of hexagon (defaults to variable-width) - * @returns a shield definition object - */ -function hexagonHorizontalShield( - angle, - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - let angleInRadians = (angle * Math.PI) / 180; - textColor = textColor ?? strokeColor; - radius = radius ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "hexagonHorizontal", - params: { - angle: angleInRadians, - fillColor: fillColor, - strokeColor: strokeColor, - radius: radius, - rectWidth: rectWidth, - }, - }, - textLayout: textConstraint("ellipse"), - padding: { - left: 3, - right: 3, - top: 2, - bottom: 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with an octagon background - * - * @param {*} offset - Height of diagonal edges - * @param {*} angle - Angle (in degrees) at which sides deviate from vertical - * @param {*} fillColor - Color of octagon background fill - * @param {*} strokeColor - Color of octagon outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} radius - Corner radius of octagon (defaults to 2) - * @param {*} rectWidth - Width of octagon (defaults to variable-width) - * @returns a shield definition object - */ -function octagonVerticalShield( - offset, - angle, - fillColor, - strokeColor, - textColor, - radius, - rectWidth -) { - let angleInRadians = (angle * Math.PI) / 180; - textColor = textColor ?? strokeColor; - radius = radius ?? 2; - return { - canvasDrawnBlank: { - drawFunc: "octagonVertical", - params: { - offset: offset, - angle: angleInRadians, - fillColor: fillColor, - strokeColor: strokeColor, - radius: radius, - rectWidth: rectWidth, - }, - }, - textLayout: textConstraint("ellipse"), - padding: { - left: 2, - right: 2, - top: 2, - bottom: 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a shield with a pill-shaped background - * - * @param {*} fillColor - Color of pill background fill - * @param {*} strokeColor - Color of pill outline stroke - * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of pill (defaults to variable-width) - * @returns a shield definition object - */ -function pillShield(fillColor, strokeColor, textColor, rectWidth) { - textColor = textColor ?? strokeColor; - return { - canvasDrawnBlank: { - drawFunc: "roundedRectangle", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - rectWidth: rectWidth, - radius: 10, - }, - }, - textLayout: textConstraint("ellipse"), - padding: { - left: 2, - right: 2, - top: 2, - bottom: 2, - }, - textColor: textColor, - }; -} - -/** - * Draws a circle icon inside a black-outlined white square shield - * - * @param {*} fillColor - Color of circle icon background fill - * @param {*} strokeColor - Color of circle icon outline - * @returns a shield definition object - */ -function paBeltShield(fillColor, strokeColor) { - return { - notext: true, - canvasDrawnBlank: { - drawFunc: "paBelt", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - }, - }, - }; -} - -/** - * Draws a rectangle icon inside a white-outlined green square shield - * - * @param {*} fillColor - Color of rectangle icon background fill - * @param {*} strokeColor - Color of rectangle icon outline - * @returns a shield definition object - */ -function bransonRouteShield(fillColor, strokeColor) { - return { - notext: true, - canvasDrawnBlank: { - drawFunc: "branson", - params: { - fillColor: fillColor, - strokeColor: strokeColor, - }, - }, - }; -} - -/** - * Adds banner text above a shield - * - * @param {*} baseDef - Shield definition object - * @param {*} modifiers - Array of strings to be displayed above shield - * @returns a shield definition object - */ -function banneredShield(baseDef, modifiers) { - return { - modifiers: modifiers, - ...baseDef, - }; -} +import { + textConstraint, + homePlateDownShield, + octagonVerticalShield, + hexagonHorizontalShield, + hexagonVerticalShield, + homePlateUpShield, + ovalShield, + circleShield, + roundedRectShield, + pillShield, + trapezoidUpShield, + trapezoidDownShield, + pentagonUpShield, + diamondShield, + triangleDownShield, + escutcheonDownShield, + fishheadDownShield, + banneredShield, + paBeltShield, + bransonRouteShield, +} from "@americana/maplibre-shield-generator"; export function loadShields() { + const shields = {}; + // Multi-use shields // Triangle shields @@ -765,13 +71,24 @@ export function loadShields() { spriteBlank: ["shield_badge_2", "shield_badge_3"], textColor: Color.shields.black, padding: { - left: 2, - right: 2, + left: 3, + right: 3, top: 4, bottom: 5, }, }; + let badgeShieldCrossbar = { + spriteBlank: ["shield_badge_crossbar_2", "shield_badge_crossbar_3"], + textColor: Color.shields.black, + padding: { + left: 3, + right: 3, + top: 6, + bottom: 4, + }, + }; + // Default shields["default"] = { @@ -786,22 +103,50 @@ export function loadShields() { }; // NORTH AMERICA + shields["GLCT"] = { + notext: true, + overrideByRef: { + LECT: { + spriteBlank: "shield_glct_lect", + }, + LHCT: { + spriteBlank: "shield_glct_lhct", + }, + LMCT: { + spriteBlank: "shield_glct_lmct", + colorLighten: Color.shields.green, + }, + LSCT: { + spriteBlank: "shield_glct_lsct", + }, + }, + }; + shields["GLCT:Loop"] = { + ...shields["GLCT"], + banners: ["LOOP"], + bannerTextColor: Color.shields.brown, + overrideByRef: { + LMCT: { + spriteBlank: "shield_glct_lmct", + colorLighten: Color.shields.brown, + }, + }, + }; // Canada shields["CA:transcanada"] = { - spriteBlank: "shield_ca_tch", - textLayout: textConstraint("ellipse"), + spriteBlank: ["shield_ca_tch_2", "shield_ca_tch_3"], textColor: Color.shields.green, padding: { - left: 2, - right: 2, - top: 5, - bottom: 4, + left: 5, + right: 5, + top: 7, + bottom: 5, }, }; shields["CA:transcanada:namedRoute"] = { noref: { - spriteBlank: "shield_ca_tch", + spriteBlank: "shield_ca_tch_2", }, }; @@ -823,10 +168,10 @@ export function loadShields() { spriteBlank: ["shield_ca_bc_2", "shield_ca_bc_3"], textColor: Color.shields.blue, padding: { - left: 3.5, - right: 3.5, - top: 5, - bottom: 4, + left: 3, + right: 3, + top: 5.5, + bottom: 4.5, }, }; @@ -842,16 +187,13 @@ export function loadShields() { Color.shields.white, 30 ); - shields["CA:MB:Winnipeg"] = { - spriteBlank: "shield_ca_mb_winnipeg", - textColor: Color.shields.black, - padding: { - left: 4, - right: 4, - top: 4, - bottom: 3, - }, - }; + shields["CA:MB:Winnipeg"] = trapezoidDownShield( + 10, + Color.shields.white, + Color.shields.black, + Color.shields.black, + 2 + ); // New Brunswick shields["CA:NB:tertiary"] = { @@ -859,7 +201,7 @@ export function loadShields() { textColor: Color.shields.black, padding: { left: 4, - right: 4, + right: 5, top: 5, bottom: 5, }, @@ -881,21 +223,63 @@ export function loadShields() { ); // Nova Scotia - shields["CA:NS:H"] = { - spriteBlank: "shield_ca_ns_h", - textColor: Color.shields.white, - padding: { - left: 2, - right: 2, - top: 6.5, - bottom: 4, - }, - }; + shields["CA:NS:H"] = escutcheonDownShield( + 12, + Color.shields.blue, + Color.shields.white + ); shields["CA:NS:T"] = badgeShield; shields["CA:NS:R"] = roundedRectShield( Color.shields.brown, Color.shields.white ); + shields["CA:NS:S"] = { + notext: true, + overrideByName: { + "A. Murray MacKay Bridge": { + spriteBlank: "shield_ca_ns_s_mkb", + }, + "Angus L. MacDonald Bridge": { + spriteBlank: "shield_ca_ns_s_mdb", + }, + "Bras d'Or Lakes Scenic Drive": { + spriteBlank: "shield_ca_ns_s_bdolsd", + }, + "Ceilidh Trail": { + spriteBlank: "shield_ca_ns_s_cet", + }, + "Cabot Trail": { + spriteBlank: "shield_ca_ns_s_ct", + }, + "Digby Neck and Islands Scenic Drive": { + spriteBlank: "shield_ca_ns_s_dnisd", + }, + "Evangeline Trail": { + spriteBlank: "shield_ca_ns_s_et", + }, + "Fleur-de-lis Trail": { + spriteBlank: "shield_ca_ns_s_fdlt", + }, + "Glooscap Trail": { + spriteBlank: "shield_ca_ns_s_gt", + }, + "Kejimkujik Scenic Drive": { + spriteBlank: "shield_ca_ns_s_ksd", + }, + "Lighthouse Route": { + spriteBlank: "shield_ca_ns_s_lr", + }, + "Marine Drive": { + spriteBlank: "shield_ca_ns_s_md", + }, + "Marconi Trail": { + spriteBlank: "shield_ca_ns_s_mt", + }, + "Sunrise Trail": { + spriteBlank: "shield_ca_ns_s_st", + }, + }, + }; // Northwest Territories shields["CA:NT"] = { @@ -917,7 +301,14 @@ export function loadShields() { left: 3.5, right: 3.5, top: 6, - bottom: 3, + bottom: 2, + }, + overrideByRef: { + QEW: { + textColor: Color.shields.blue, + colorLighten: Color.shields.blue, + colorDarken: Color.shields.yellow, + }, }, }; shields["CA:ON:primary:Toll"] = { @@ -1042,6 +433,32 @@ export function loadShields() { shields["CA:ON:Muskoka:West"] = banneredShield(shields["CA:ON:Muskoka"], [ "WEST", ]); + shields["CA:ON:Hamilton:Expressway"] = { + notext: true, + overrideByName: { + "Lincoln M. Alexander Parkway": { + spriteBlank: "shield_ca_on_hamilton_blue", + }, + "Red Hill Valley Parkway": { + spriteBlank: "shield_ca_on_hamilton_green", + }, + }, + }; + shields["CA:ON:Toronto:Expressway"] = { + spriteBlank: "shield_ca_on_toronto", + textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), + padding: { + left: 5, + right: 5, + top: 5, + bottom: 5, + }, + refsByName: { + "Don Valley Parkway": "DV", + "Gardiner Expressway": "G", + }, + }; // Prince Edward Island shields["CA:PE"] = { @@ -1063,7 +480,7 @@ export function loadShields() { left: 2, right: 2, top: 5.5, - bottom: 4, + bottom: 4.5, }, }; shields["CA:QC:R"] = { @@ -1100,7 +517,16 @@ export function loadShields() { ); // Yukon - shields["CA:YT"] = roundedRectShield(Color.shields.white, Color.shields.red); + shields["CA:YT"] = { + ...roundedRectShield(Color.shields.white, Color.shields.red), + overrideByRef: { + 2: roundedRectShield(Color.shields.white, "#ce9d00"), + 3: roundedRectShield(Color.shields.white, "#ce9d00"), + 5: roundedRectShield(Color.shields.white, Color.shields.blue), + 6: roundedRectShield(Color.shields.white, Color.shields.green), + 11: roundedRectShield(Color.shields.white, Color.shields.blue), + }, + }; // Haiti shields["HT:RN-road"] = shields["HT:RD-road"] = roundedRectShield( @@ -1114,12 +540,55 @@ export function loadShields() { shields["MX:MX"] = { spriteBlank: "shield_mx_mx", padding: { - left: 2, + left: 2, + right: 2, + top: 3, + bottom: 2, + }, + }; + + // Ejes Viales (CDMX) + shields["MX:CDMX:EJE:CENTRAL"] = { + noref: { + spriteBlank: "shield_mx_cdmx_eje_central", + }, + }; + shields["MX:CDMX:EJE:NTE"] = { + spriteBlank: "shield_mx_cdmx_eje_nte", + padding: { + left: 2, + right: 2, + top: 8, + bottom: 2, + }, + }; + shields["MX:CDMX:EJE:OTE"] = { + spriteBlank: "shield_mx_cdmx_eje_ote", + padding: { + left: 2, + right: 8, + top: 2, + bottom: 2, + }, + }; + shields["MX:CDMX:EJE:PTE"] = { + spriteBlank: "shield_mx_cdmx_eje_pte", + padding: { + left: 8, right: 2, - top: 3, + top: 2, bottom: 2, }, }; + shields["MX:CDMX:EJE:SUR"] = { + spriteBlank: "shield_mx_cdmx_eje_sur", + padding: { + left: 2, + right: 2, + top: 2, + bottom: 8, + }, + }; // United States @@ -1138,6 +607,7 @@ export function loadShields() { shields["US:I:Alternate"] = banneredShield(shields["US:I"], ["ALT"]); shields["US:I:Future"] = banneredShield(shields["US:I"], ["FUT"]); + shields["US:I:Spur"] = banneredShield(shields["US:I"], ["SPUR"]); shields["US:I:Truck"] = banneredShield(shields["US:I"], ["TRK"]); shields["US:I:Express"] = banneredShield(shields["US:I"], ["EXPR"]); shields["US:I:Express:Toll"] = shields["US:I:Express"]; @@ -1169,6 +639,8 @@ export function loadShields() { shields["US:US:Bypass"] = banneredShield(shields["US:US"], ["BYP"]); + shields["US:US:Future"] = banneredShield(shields["US:US"], ["FUT"]); + shields["US:US:Business"] = banneredShield(shields["US:US"], ["BUS"]); shields["US:US:Business:Alternate"] = banneredShield(shields["US:US"], [ "BUS", @@ -1186,64 +658,83 @@ export function loadShields() { "BUS", ]); - shields["US:US:Historic"] = { - ...badgeShield, - textColor: Color.shields.brown, - colorLighten: Color.shields.brown, - }; + shields["US:US:Historic"] = banneredShield( + { + ...badgeShieldCrossbar, + textColor: Color.shields.brown, + colorLighten: Color.shields.brown, + }, + ["HIST"], + Color.shields.brown + ); - // Federal Agencies + // US Federal Agencies + + // Bureau of Indian Affairs shields["US:BIA"] = { spriteBlank: "shield_us_bia", textColor: Color.shields.black, - textLayout: textConstraint("southHalfEllipse"), + textLayout: textConstraint("ellipse"), padding: { - left: 4, - right: 4, - top: 4, + left: 1, + right: 1, + top: 3, bottom: 5, }, }; + // Department of Energy: see California + // National Park Service shields["US:NPS:Blue_Ridge"] = { noref: { spriteBlank: "shield_us_nps_brp", }, }; - shields["US:NPS:Natchez_Trace"] = { noref: { spriteBlank: "shield_us_nps_ntp", }, }; - shields["US:GRR"] = { + // Other multistate routes + + // Great Lakes Seaway Trail + shields["US:GLST"] = { noref: { - spriteBlank: "shield_us_grr", + spriteBlank: "shield_us_glst", }, }; - shields["US:GLST"] = { + // Great River Road + shields["US:GRR"] = { noref: { - spriteBlank: "shield_us_glst", + spriteBlank: "shield_us_grr", }, }; - shields["GLCT"] = { + // Lincoln Heritage Trail + shields["US:LHT"] = { + spriteBlank: "shield_us_lht", + notext: true, + }; + + // Ohio River Scenic Byway + shields["US:ORSB"] = { + spriteBlank: ["shield_us_orsb"], notext: true, }; - shields["GLCT:Loop"] = banneredShield(shields["GLCT"], ["LOOP"]); + // US States // Alaska shields["US:AK"] = { spriteBlank: "shield_us_ak", textColor: Color.shields.black, padding: { - left: 5, - right: 1, - top: 1, - bottom: 8, + left: 5.5, + right: 1.5, + top: 1.5, + bottom: 9, }, }; @@ -1336,6 +827,20 @@ export function loadShields() { Color.shields.yellow )) ); + shields["US:AL:Baldwin:Baldwin_Beach_Express"] = { + ...roundedRectShield(Color.shields.green, Color.shields.white), + ref: "BBX", + }; + shields["US:AL:Baldwin:Foley_Beach_Express"] = { + spriteBlank: "shield_us_al_foley", + notext: true, + }; + + // Arizona + shields["US:AZ:Scenic"] = { + spriteBlank: "shield_us_az_scenic", + notext: true, + }; // Arkansas shields["US:AR"] = { @@ -1347,6 +852,13 @@ export function loadShields() { top: 4, bottom: 5, }, + overrideByRef: { + 980: { + textColor: Color.shields.white, + colorLighten: Color.shields.white, + colorDarken: Color.shields.blue, + }, + }, }; [ "Calhoun", @@ -1445,12 +957,19 @@ export function loadShields() { bottom: 4, }, }; - shields["US:CA:Business"] = banneredShield(shields["US:CA"], ["BUS"]); - shields["US:CA:CR"] = pentagonUpShield( - 3, - 15, - Color.shields.blue, - Color.shields.yellow + shields["US:CA:Business"] = banneredShield( + shields["US:CA"], + ["BUS"], + Color.shields.green + ); + ["CR", "Sierra"].forEach( + (county) => + (shields[`US:CA:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow + )) ); shields["US:CA:Mendocino"] = roundedRectShield( Color.shields.green, @@ -1461,6 +980,9 @@ export function loadShields() { notext: true, }; + // Department of Energy + shields["US:DOE:Hanford"] = shields["US:CA"]; + // Colorado shields["US:CO"] = { spriteBlank: "shield_us_co", @@ -1478,7 +1000,7 @@ export function loadShields() { padding: { left: 2, right: 2, - top: 9.5, + top: 10, bottom: 2, }, }; @@ -1521,15 +1043,23 @@ export function loadShields() { Color.shields.green, Color.shields.white ); + shields["US:CO:Weld:WCP"] = { + ...pentagonUpShield(3, 15, Color.shields.blue, Color.shields.green), + textHaloColor: Color.shields.white, + }; // Connecticut shields["US:CT"] = roundedRectShield( Color.shields.white, Color.shields.black ); - shields["US:CT:Parkway"] = { notext: true, + overrideByName: { + "Merritt Parkway": { + spriteBlank: "shield_us_ct_parkway_merritt", + }, + }, }; // Washington, D.C. @@ -1583,6 +1113,7 @@ export function loadShields() { Color.shields.blue, Color.shields.yellow ); + shields["US:FL:CR:Truck"] = banneredShield(shields["US:FL:CR"], ["TRK"]); // Georgia shields["US:GA"] = { @@ -1594,6 +1125,16 @@ export function loadShields() { top: 5, bottom: 4, }, + overrideByRef: { + 515: { + textColor: Color.shields.blue, + colorLighten: Color.shields.blue, + }, + 520: { + textColor: Color.shields.green, + colorLighten: Color.shields.green, + }, + }, }; shields["US:GA:Truck:Bypass"] = banneredShield(shields["US:GA"], [ "TRK", @@ -1636,12 +1177,13 @@ export function loadShields() { spriteBlank: ["shield_us_id_2", "shield_us_id_3"], textColor: Color.shields.black, padding: { - left: 5, - right: 1, - top: 1, - bottom: 8, + left: 5.5, + right: 1.5, + top: 1.5, + bottom: 9, }, }; + shields["US:ID:Business"] = banneredShield(shields["US:ID"], ["BUS"]); // Illinois shields["US:IL"] = roundedRectShield( @@ -1715,18 +1257,24 @@ export function loadShields() { spriteBlank: "shield_us_in_toll", }, }; + shields["US:IN:JHMHT"] = { + spriteBlank: ["shield_us_in_jhmht"], + notext: true, + }; // Kansas shields["US:KS"] = { spriteBlank: ["shield_us_ks_2", "shield_us_ks_3"], textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), padding: { - left: 4, - right: 4, - top: 4, - bottom: 4, + left: 2, + right: 2, + top: 2, + bottom: 2, }, }; + shields["US:KS:Truck"] = banneredShield(shields["US:KS"], ["TRK"]); shields["US:KS:Turnpike"] = { noref: { spriteBlank: "shield_us_ks_turnpike", @@ -1767,6 +1315,15 @@ export function loadShields() { top: 2, bottom: 6, }, + refsByName: { + "Audubon Parkway": "AU", + "Bluegrass Parkway": "BG", + "Cumberland Parkway": "LN", + "Hal Rogers Parkway": "HR", + "Mountain Parkway": "MP", + "Purchase Parkway": "JC", + "Western Kentucky Parkway": "WK", + }, }; // Louisiana @@ -1803,8 +1360,8 @@ export function loadShields() { "Webster", "Winn", ].forEach( - (county) => - (shields[`US:LA:${county}`] = pentagonUpShield( + (parish) => + (shields[`US:LA:${parish}`] = pentagonUpShield( 3, 15, Color.shields.blue, @@ -1832,10 +1389,10 @@ export function loadShields() { spriteBlank: ["shield_us_md_2", "shield_us_md_3"], textColor: Color.shields.black, padding: { - left: 4, - right: 4, - top: 7, - bottom: 3, + left: 2, + right: 2, + top: 6, + bottom: 2, }, }; shields["US:MD:Alternate"] = banneredShield(shields["US:MD"], ["ALT"]); @@ -1846,7 +1403,8 @@ export function loadShields() { textColor: Color.shields.green, colorLighten: Color.shields.green, }, - ["BUS"] + ["BUS"], + Color.shields.green ); // Maine @@ -1862,6 +1420,15 @@ export function loadShields() { // Michigan shields["US:MI"] = diamondShield(Color.shields.white, Color.shields.black); + shields["US:MI"].overrideByRef = { + 185: diamondShield( + Color.shields.brown, + Color.shields.white, + Color.shields.white, + 0, + 24 + ), + }; shields["US:MI:Business"] = banneredShield(shields["US:MI"], ["BUS"]); shields["US:MI:Connector"] = banneredShield(shields["US:MI"], ["CONN"]); ["CR", "Benzie", "Gogebic", "Kalkaska", "Montcalm", "Roscommon"].forEach( @@ -1899,59 +1466,26 @@ export function loadShields() { bottom: 3, }, }; + shields["US:MN:Intercounty"] = roundedRectShield( + Color.shields.blue, + Color.shields.yellow, + Color.shields.white + ); [ - "Aitkin", - "Anoka", - "Becker", - "Beltrami", - "Benton", - "Big_Stone", - "Blue_Earth", "Brown", - "Carlton", - "Carver", - "Cass", - "Chippewa", "Chisago", - "Clay", "Clearwater", - "Cook", "Cottonwood", - "Crow_Wing", "Dakota", - "Dodge", - "Douglas", - "Faribault", - "Fillmore", "Freeborn", "Goodhue", - "Grant", - "Hennepin", - "Houston", - "Hubbard", "Isanti", - "Itasca", "Jackson", - "Kanabec", - "Kandiyohi", "Kittson", - "Koochiching", "Lac_qui_Parle", - "Lake", - "Lake_of_the_Woods", - "Le_Sueur", - "Lincoln", - "Lyon", - "Mahnomen", - "Marshall", - "Martin", "McLeod", "Meeker", - "Mille_Lacs", "Morrison", - "Mower", - "Murray", - "Nicollet", "Nobles", "Norman", "Olmsted", @@ -1960,40 +1494,24 @@ export function loadShields() { "Pine", "Pipestone", "Polk", - "Pope", - "Ramsey", "Red_Lake", "Redwood", "Renville", - "Rice", - "Rock", "Roseau", "Saint_Louis", - "Scott", - "Sherburne", "Sibley", "Stearns", - "Steele", "Stevens", - "Swift", "Todd", "Traverse", "Wabasha", "Wadena", - "Waseca", "Washington", - "Watonwan", - "Wilkin", - "Winona", "Wright", "Yellow_Medicine", ].forEach( (county) => - ([ - shields[`US:MN:${county}:CSAH`], - shields[`US:MN:${county}:CR`], - shields[`US:MN:${county}:Park_Access`], - ] = [ + ([shields[`US:MN:${county}:CSAH`], shields[`US:MN:${county}:CR`]] = [ pentagonUpShield( 3, 15, @@ -2002,15 +1520,88 @@ export function loadShields() { Color.shields.white ), roundedRectShield(Color.shields.white, Color.shields.black), - trapezoidDownShield( - 10, - Color.shields.brown, - Color.shields.white, - Color.shields.white, - 2 - ), ]) ); + [ + "Anoka", + "Blue_Earth", + "Fillmore", + "Hennepin", + "Kandiyohi", + "Lincoln", + "Mower", + "Murray", + "Pope", + "Ramsey", + "Scott", + "Wilkin", + ].forEach((county) => + ["CSAH", "CR"].forEach( + (network) => + (shields[`US:MN:${county}:${network}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.white + )) + ) + ); + [ + "Aitkin", + "Becker", + "Beltrami", + "Benton", + "Big_Stone", + "Carlton", + "Carver", + "Cass", + "Chippewa", + "Clay", + "Cook", + "Crow_Wing", + "Dodge", + "Douglas", + "Faribault", + "Grant", + "Houston", + "Hubbard", + "Itasca", + "Kanabec", + "Koochiching", + "Lake", + "Lake_of_the_Woods", + "Le_Sueur", + "Lyon", + "Mahnomen", + "Marshall", + "Martin", + "Mille_Lacs", + "Nicollet", + "Rice", + "Rock", + "Sherburne", + "Steele", + "Swift", + "Waseca", + "Watonwan", + "Winona", + ].forEach((county) => + ["CSAH", "CR"].forEach( + (network) => + (shields[`US:MN:${county}:${network}`] = roundedRectShield( + Color.shields.white, + Color.shields.black + )) + ) + ); + shields[`US:MN:Hennepin:Park_Access`] = trapezoidDownShield( + 10, + Color.shields.brown, + Color.shields.white, + Color.shields.white, + 2 + ); // Missouri shields["US:MO"] = { @@ -2018,9 +1609,9 @@ export function loadShields() { textColor: Color.shields.black, padding: { left: 4, - right: 4.5, - top: 3, - bottom: 5.5, + right: 4, + top: 2, + bottom: 5, }, }; shields["US:MO:Alternate"] = banneredShield(shields["US:MO"], ["ALT"]); @@ -2058,7 +1649,16 @@ export function loadShields() { Color.shields.brown, Color.shields.white ); - shields["US:MO:Taney:Branson"] = {}; // See ref-specific cases below + shields["US:MO:Taney:Branson"] = { + overrideByRef: { + "Red Route": bransonRouteShield(Color.shields.red, Color.shields.white), + "Yellow Route": bransonRouteShield( + Color.shields.yellow, + Color.shields.green + ), + "Blue Route": bransonRouteShield(Color.shields.blue, Color.shields.white), + }, + }; // Northern Mariana Islands shields["US:MP"] = { @@ -2074,6 +1674,11 @@ export function loadShields() { // Mississippi shields["US:MS"] = ovalShield(Color.shields.white, Color.shields.black); + shields["US:MS:Scenic"] = banneredShield( + ovalShield(Color.shields.white, Color.shields.blue), + ["SCEN"], + Color.shields.blue + ); [ "Alcorn", "Calhoun", @@ -2103,11 +1708,12 @@ export function loadShields() { shields["US:MT:secondary"] = { spriteBlank: "shield_us_mt_secondary", textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), padding: { - left: 2, - right: 2, - top: 0, - bottom: 6, + left: 1, + right: 1, + top: 4, + bottom: 8, }, }; ["Dawson", "Richland"].forEach( @@ -2151,6 +1757,7 @@ export function loadShields() { }; shields["US:ND:Alternate"] = banneredShield(shields["US:ND"], ["ALT"]); shields["US:ND:Business"] = banneredShield(shields["US:ND"], ["BUS"]); + shields["US:ND:Bypass"] = banneredShield(shields["US:ND"], ["BYP"]); shields["US:ND:Truck"] = banneredShield(shields["US:ND"], ["TRK"]); [ "Barnes", @@ -2229,13 +1836,26 @@ export function loadShields() { spriteBlank: ["shield_us_nh_2", "shield_us_nh_3"], textColor: Color.shields.black, padding: { - left: 3.5, + left: 4, right: 2, top: 4, bottom: 5, }, }; shields["US:NH:Bypass"] = banneredShield(shields["US:NH"], ["BYP"]); + shields["US:NH:Turnpike"] = { + notext: true, + overrideByName: { + "Everett Turnpike": { + spriteBlank: "shield_us_nh_turnpike", + colorLighten: Color.shields.green, + }, + "Spaulding Turnpike": { + spriteBlank: "shield_us_nh_turnpike", + colorLighten: Color.shields.blue, + }, + }, + }; // New Jersey shields["US:NJ"] = ovalShield(Color.shields.white, Color.shields.black); @@ -2243,9 +1863,11 @@ export function loadShields() { spriteBlank: "shield_us_nj_ace_noref", notext: true, }; - shields["US:NJ:ACE:Connector"] = banneredShield(shields["US:NJ:ACE"], [ - "CONN", - ]); + shields["US:NJ:ACE:Connector"] = banneredShield( + shields["US:NJ:ACE"], + ["CONN"], + Color.shields.blue + ); shields["US:NJ:GSP"] = { spriteBlank: "shield_us_nj_gsp_noref", notext: true, @@ -2290,15 +1912,36 @@ export function loadShields() { Color.shields.white, Color.shields.black ); - shields["US:NJ:CR:Spur"] = banneredShield(shields["US:NJ:CR"], ["SPUR"]); - shields["US:NJ:CR:Truck"] = banneredShield(shields["US:NJ:CR"], ["TRK"]); + shields["US:NJ:CR:Spur"] = banneredShield( + shields["US:NJ:CR"], + ["SPUR"], + Color.shields.blue + ); + shields["US:NJ:CR:Truck"] = banneredShield( + shields["US:NJ:CR"], + ["TRK"], + Color.shields.blue + ); + ["Sussex", "Warren"].forEach( + (county) => + (shields[`US:NJ:${county}:NPS`] = pillShield( + Color.shields.brown, + Color.shields.white + )) + ); // New Mexico - shields["US:NM"] = pillShield( - Color.shields.white, - Color.shields.pink, - Color.shields.black - ); + shields["US:NM"] = { + spriteBlank: ["shield40_us_nm_2", "shield40_us_nm_3"], + textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), + padding: { + left: 5, + right: 5, + top: 5, + bottom: 5, + }, + }; shields["US:NM:Frontage"] = { spriteBlank: "shield_us_nm_frontage", textColor: Color.shields.black, @@ -2348,11 +1991,12 @@ export function loadShields() { shields["US:NV"] = { spriteBlank: "shield_us_nv", textColor: Color.shields.black, + textLayout: textConstraint("triangleDown"), padding: { - left: 2, - right: 2, + left: -2, + right: -2, top: 2, - bottom: 12, + bottom: 6, }, }; ["Clark", "Washoe"].forEach( @@ -2376,6 +2020,23 @@ export function loadShields() { bottom: 5, }, }; + shields["US:NY:Truck"] = banneredShield(shields["US:NY"], ["TRK"]); + shields["US:NY:Inner_Loop"] = { + ...trapezoidDownShield( + 10, + Color.shields.white, + Color.shields.black, + Color.shields.black, + 4 + ), + padding: { + left: 4, + right: 4, + top: 3, + bottom: 3, + }, + }; + shields["US:NY:Inner_Loop"].ref = "LOOP"; shields["US:NY:Thruway"] = { noref: { spriteBlank: "shield_us_ny_thruway", @@ -2391,6 +2052,18 @@ export function loadShields() { textColor: Color.shields.white, colorLighten: Color.shields.white, colorDarken: Color.shields.green, + refsByName: { + "Bear Mountain State Parkway": "BMP", + "Bronx River Parkway": "BRP", + "Cross County Parkway": "CCP", + "Hutchinson River Parkway": "HRP", + "Korean War Veterans Parkway": "KWVP", + "Mosholu Parkway": "MP", + "Niagara Scenic Parkway": "NSP", + "Saw Mill River Parkway": "SMP", + "Sprain Brook Parkway": "SBP", + "Taconic State Parkway": "TSP", + }, }; shields["US:NY:Parkway:LI"] = { spriteBlank: "shield_us_ny_parkway_li", @@ -2593,11 +2266,12 @@ export function loadShields() { shields["US:OH:ASD"] = { spriteBlank: ["shield_us_oh_asd"], textColor: Color.shields.green, + textLayout: textConstraint("triangleDown"), padding: { - left: 6, - right: 3, + left: 1, + right: 1, top: 4, - bottom: 7, + bottom: 2, }, }; shields["US:OH:HOL"] = { @@ -2614,7 +2288,7 @@ export function loadShields() { spriteBlank: ["shield_us_oh_sci_2", "shield_us_oh_sci_3"], textColor: Color.shields.black, padding: { - left: 3, + left: 2, right: 3, top: 4, bottom: 6, @@ -2633,10 +2307,10 @@ export function loadShields() { spriteBlank: ["shield_us_oh_tus_salem"], textColor: Color.shields.black, padding: { - left: 1, + left: 2, right: 4, - top: 1, - bottom: 4, + top: 2, + bottom: 5, }, }; @@ -2677,6 +2351,10 @@ export function loadShields() { (shields[`US:OH:${countyAndTownship[0]}:${countyAndTownship[1]}`] = banneredShield(shields[`US:OH:${countyAndTownship[0]}`], ["TWP"])) ); + shields["US:OH:JHMHT"] = { + spriteBlank: ["shield_us_oh_jhmht"], + notext: true, + }; // Oklahoma shields["US:OK"] = { @@ -2704,11 +2382,12 @@ export function loadShields() { shields["US:OR"] = { spriteBlank: ["shield_us_or_2", "shield_us_or_3"], textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), padding: { - left: 3, - right: 3, - top: 4, - bottom: 6.5, + left: 1, + right: 1, + top: 1, + bottom: 4, }, }; shields["US:OR:Business"] = banneredShield(shields["US:OR"], ["BUS"]); @@ -2753,7 +2432,16 @@ export function loadShields() { colorDarken: Color.shields.green, }, }; - shields["US:PA:Allegheny:Belt"] = {}; // See ref-specific cases below + shields["US:PA:Allegheny:Belt"] = { + overrideByRef: { + "Red Belt": paBeltShield(Color.shields.red, Color.shields.black), + "Orange Belt": paBeltShield(Color.shields.orange, Color.shields.black), + "Yellow Belt": paBeltShield(Color.shields.yellow, Color.shields.black), + "Green Belt": paBeltShield(Color.shields.green, Color.shields.white), + "Blue Belt": paBeltShield(Color.shields.blue, Color.shields.white), + "Purple Belt": paBeltShield(Color.shields.purple, Color.shields.white), + }, + }; // Puerto Rico shields["US:PR:primary"] = escutcheonDownShield( @@ -2794,9 +2482,26 @@ export function loadShields() { bottom: 3, }, }; - shields["US:SC:Truck"] = banneredShield(shields["US:SC"], ["TRK"]); - shields["US:SC:Business"] = banneredShield(shields["US:SC"], ["BUS"]); - shields["US:SC:Alternate"] = banneredShield(shields["US:SC"], ["ALT"]); + shields["US:SC:Truck"] = banneredShield( + shields["US:SC"], + ["TRK"], + Color.shields.blue + ); + shields["US:SC:Business"] = banneredShield( + shields["US:SC"], + ["BUS"], + Color.shields.blue + ); + shields["US:SC:Alternate"] = banneredShield( + shields["US:SC"], + ["ALT"], + Color.shields.blue + ); + shields["US:SC:Connector"] = banneredShield( + shields["US:SC"], + ["CONN"], + Color.shields.blue + ); // South Dakota shields["US:SD"] = { @@ -2809,6 +2514,12 @@ export function loadShields() { bottom: 5, }, }; + shields["US:SD:Business"] = banneredShield(shields["US:SD"], ["BUS"]); + shields["US:SD:Truck"] = banneredShield(shields["US:SD"], ["TRK"]); + shields["US:SD:Secondary"] = roundedRectShield( + Color.shields.white, + Color.shields.black + ); [ "Beadle", "Bon_Homme", @@ -2859,6 +2570,21 @@ export function loadShields() { Color.shields.white )) ); + shields["US:SD:Custer:CSP"] = { + spriteBlank: "shield_us_sd_csp", + textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), + padding: { + left: 8, + right: 7, + top: 2, + bottom: 8, + }, + }; + shields["US:SD:Custer:NPS"] = roundedRectShield( + Color.shields.brown, + Color.shields.yellow + ); // Tennessee shields["US:TN:primary"] = { @@ -2933,7 +2659,8 @@ export function loadShields() { textColor: Color.shields.brown, colorLighten: Color.shields.brown, }, - ["R"] + ["R"], + Color.shields.brown ); shields["US:TX:NASA"] = banneredShield(shields["US:TX"], ["NASA"]); @@ -2942,22 +2669,31 @@ export function loadShields() { Color.shields.blue, Color.shields.white ); - shields["US:TX:Express:Toll"] = banneredShield(shields["US:TX:Toll"], [ - "EXPR", - ]); - shields["US:TX:Loop:Toll"] = banneredShield(shields["US:TX:Toll"], ["LOOP"]); - shields["US:TX:Loop:Express:Toll"] = banneredShield(shields["US:TX:Toll"], [ - "EXPR", - "LOOP", - ]); + shields["US:TX:Express:Toll"] = banneredShield( + shields["US:TX:Toll"], + ["EXPR"], + Color.shields.blue + ); + shields["US:TX:Loop:Toll"] = banneredShield( + shields["US:TX:Toll"], + ["LOOP"], + Color.shields.blue + ); + shields["US:TX:Loop:Express:Toll"] = banneredShield( + shields["US:TX:Toll"], + ["EXPR", "LOOP"], + Color.shields.blue + ); shields["US:TX:CTRMA"] = roundedRectShield( Color.shields.blue, Color.shields.yellow, Color.shields.white ); - shields["US:TX:CTRMA:Express"] = banneredShield(shields["US:TX:CTRMA"], [ - "EXPR", - ]); + shields["US:TX:CTRMA:Express"] = banneredShield( + shields["US:TX:CTRMA"], + ["EXPR"], + Color.shields.blue + ); shields["US:TX:Montgomery:MCTRA"] = homePlateDownShield( 5, Color.shields.blue, @@ -2973,17 +2709,21 @@ export function loadShields() { top: 2, bottom: 8, }, + refsByName: { + "Fort Bend Parkway Toll Road": "FBP", + "Fort Bend Westpark Tollway": "WPT", + }, + }; + shields["US:TX:Harris:HCTRA"] = { + ...pentagonUpShield(3, 15, Color.shields.purple, Color.shields.yellow), + refsByName: { + "Sam Houston Tollway": "SHT", + "Fort Bend Toll Road": "FBTR", + "Hardy Toll Road": "HTR", + "Tomball Tollway": "TBT", + "Westpark Tollway": "WPT", + }, }; - shields["US:TX:Harris:HCTRA"] = pentagonUpShield( - 3, - 15, - Color.shields.purple, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0, - 28 - ); // Texas county roads [ @@ -3042,11 +2782,13 @@ export function loadShields() { ); shields["US:TX:Jackson"] = banneredShield( roundedRectShield(Color.shields.blue, Color.shields.white), - ["CR"] + ["CR"], + Color.shields.blue ); shields["US:TX:Andrews:Andrews:Loop"] = banneredShield( roundedRectShield(Color.shields.white, Color.shields.blue), - ["LOOP"] + ["LOOP"], + Color.shields.blue ); // Utah @@ -3056,15 +2798,18 @@ export function loadShields() { padding: { left: 4, right: 4, - top: 5.5, + top: 6, bottom: 5, }, }; - shields["US:UT:Wayne"] = pentagonUpShield( - 3, - 15, - Color.shields.blue, - Color.shields.yellow + ["Wayne", "Washington"].forEach( + (county) => + (shields[`US:UT:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow + )) ); // Virginia @@ -3096,7 +2841,17 @@ export function loadShields() { bottom: 2, }, }; - shields["US:VT:Alternate"] = banneredShield(shields["US:VT"], ["ALT"]); + shields["US:VT:Alternate"] = banneredShield( + shields["US:VT"], + ["ALT"], + Color.shields.green + ); + shields["US:VT:Truck"] = banneredShield( + shields["US:VT"], + ["TRK"], + Color.shields.green + ); + // Vermont routes town maintained sections - black and white ovals shields["US:VT:Town"] = ovalShield(Color.shields.white, Color.shields.black); @@ -3104,17 +2859,23 @@ export function loadShields() { shields["US:WA"] = { spriteBlank: "shield_us_wa", textColor: Color.shields.black, + textLayout: textConstraint("ellipse"), padding: { - left: 3, - right: 4, - top: 3, - bottom: 7, + left: 2, + right: 3, + top: 2, + bottom: 6, }, }; shields["US:WA:Spur"] = banneredShield(shields["US:WA"], ["SPUR"]); shields["US:WA:Business"] = banneredShield(shields["US:WA"], ["BUS"]); shields["US:WA:Alternate"] = banneredShield(shields["US:WA"], ["ALT"]); + shields["US:WA:Asotin"] = roundedRectShield( + Color.shields.green, + Color.shields.white + ); + // Wisconsin shields["US:WI"] = { spriteBlank: ["shield_us_wi_2", "shield_us_wi_3"], @@ -3233,9 +2994,12 @@ export function loadShields() { Color.shields.black ); shields["US:WV:HARP"] = homePlateUpShield( - 5, + 6, Color.shields.white, - Color.shields.black + Color.shields.black, + Color.shields.black, + 0, + 0 ); // Wyoming @@ -3244,6 +3008,7 @@ export function loadShields() { Color.shields.black ); [ + "Albany", "Big_Horn", "Carbon", "Converse", @@ -3271,13 +3036,6 @@ export function loadShields() { )) ); - // Multistate auto trails - - shields["US:LHT"] = { - spriteBlank: "shield_us_lht", - notext: true, - }; - // SOUTH AMERICA // Chile @@ -3285,8 +3043,8 @@ export function loadShields() { spriteBlank: ["shield_badge_2", "shield_badge_3"], textColor: Color.shields.white, padding: { - left: 2, - right: 2, + left: 3, + right: 3, top: 4, bottom: 5, }, @@ -3307,7 +3065,7 @@ export function loadShields() { left: 4, right: 4, top: 3, - bottom: 6, + bottom: 7, }, }; @@ -3384,6 +3142,18 @@ export function loadShields() { shields["GH:regional"] = roundedRectShield(Color.shields.yellow, Color.shields.black); + // Madagascar + shields["MG:RN-road"] = roundedRectShield( + Color.shields.red, + Color.shields.white + ); + + // Niger + shields["NE:N-roads"] = roundedRectShield( + Color.shields.red, + Color.shields.white + ); + // ASIA // Armenia @@ -3618,12 +3388,11 @@ export function loadShields() { // South Korea shields["KR:expressway"] = { spriteBlank: ["shield_kr_expressway_2", "shield_kr_expressway_3"], - textLayout: textConstraint("southHalfEllipse"), textColor: Color.shields.white, padding: { left: 4, right: 4, - top: 8, + top: 6, bottom: 4, }, }; @@ -3712,6 +3481,10 @@ export function loadShields() { 0, 34 ); + shields["TR:national"] = roundedRectShield( + Color.shields.blue, + Color.shields.white + ); // Taiwan shields["TW:freeway"] = { @@ -3748,9 +3521,7 @@ export function loadShields() { // EUROPE shields["e-road"] = roundedRectShield( Color.shields.green, - Color.shields.white, - Color.shields.white, - 34 + Color.shields.white ); // Austria @@ -3798,6 +3569,10 @@ export function loadShields() { shields["BE:B-road"] = shields["BE:R-road"] = roundedRectShield(Color.shields.white, Color.shields.black); + shields["BE:VLG:Ring_Antwerpen"] = roundedRectShield( + Color.shields.yellow, + Color.shields.black + ); // Bulgaria shields["bg:motorway"] = roundedRectShield( @@ -3904,6 +3679,31 @@ export function loadShields() { 34 ); + // Croatia + shields["HR:Autoceste"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); + shields["HR:Državne ceste"] = roundedRectShield( + Color.shields.blue, + Color.shields.white, + Color.shields.white + ); + shields["HR:Županijske ceste"] = roundedRectShield( + Color.shields.yellow, + Color.shields.black, + Color.shields.black + ); + shields["HR:Lokalne ceste"] = roundedRectShield( + Color.shields.white, + Color.shields.black, + Color.shields.black + ); + // Iceland shields["IS"] = roundedRectShield( Color.shields.white, @@ -3920,6 +3720,7 @@ export function loadShields() { shields["omt-ie-national"] = roundedRectShield( Color.shields.green, + Color.shields.white, Color.shields.yellow ); @@ -3990,10 +3791,10 @@ export function loadShields() { 34 ); - // Netherlands + // Netherlands, Kingdom of the (European Netherlands, Aruba, and Curacao) // https://wiki.openstreetmap.org/wiki/The_Netherlands_road_network shields["NL:A"] = roundedRectShield(Color.shields.red, Color.shields.white); - shields["NL:N"] = roundedRectShield( + shields["NL:N"] = shields["AW:route"] = roundedRectShield( Color.shields.yellow, Color.shields.black ); @@ -4015,7 +3816,7 @@ export function loadShields() { "Rotterdam", "Zaanstad", ].forEach((city) => (shields[`NL:S:${city}`] = nlCityRoute)); - shields["NL:binnenstedelijke_ring"] = nlCityRoute; // for both Netherlands and Curacao + shields["NL:binnenstedelijke_ring"] = nlCityRoute; // for both European Netherlands and Curacao [ "Ommen", "Schouwen", @@ -4135,6 +3936,7 @@ export function loadShields() { shields["omt-gb-trunk"] = roundedRectShield( Color.shields.green, + Color.shields.white, Color.shields.yellow ); @@ -4170,11 +3972,15 @@ export function loadShields() { 3, 15, Color.shields.brown, - Color.shields.white + Color.shields.white, + Color.shields.white, + 2, + 2 ); shields[`AU:${state_or_territory}:ALT`] = banneredShield( roundedRectShield(Color.shields.green, Color.shields.yellow), - ["ALT"] + ["ALT"], + Color.shields.green ); shields[`AU:${state_or_territory}:ALT_NR`] = banneredShield( homePlateDownShield(5, Color.shields.white, Color.shields.black), @@ -4182,7 +3988,8 @@ export function loadShields() { ); shields[`AU:${state_or_territory}:ALT_S`] = banneredShield( fishheadDownShield(Color.shields.blue, Color.shields.white), - ["ALT"] + ["ALT"], + Color.shields.blue ); } ); @@ -4209,152 +4016,16 @@ export function loadShields() { ); shields["NZ:WRR"] = circleShield(Color.shields.white, Color.shields.black); - // Ref-specific cases. Each entry should be documented in CONTRIBUTE.md - - shields["CA:ON:primary"].overrideByRef = { - QEW: { - textColor: Color.shields.blue, - colorLighten: Color.shields.blue, - colorDarken: Color.shields.yellow, - }, - }; - - shields["CA:YT"].overrideByRef = { - 2: roundedRectShield(Color.shields.white, "#ce9d00"), - 3: roundedRectShield(Color.shields.white, "#ce9d00"), - 5: roundedRectShield(Color.shields.white, Color.shields.blue), - 6: roundedRectShield(Color.shields.white, Color.shields.green), - 11: roundedRectShield(Color.shields.white, Color.shields.blue), - }; - - shields["US:AR"].overrideByRef = { - 980: { - textColor: Color.shields.white, - colorLighten: Color.shields.white, - colorDarken: Color.shields.blue, - }, - }; - - shields["US:GA"].overrideByRef = { - 515: { - textColor: Color.shields.blue, - colorLighten: Color.shields.blue, - }, - 520: { - textColor: Color.shields.green, - colorLighten: Color.shields.green, - }, - }; - - shields["US:KY:Parkway"].refsByWayName = { - // FIXME: This object contains both spelled-out and abbreviated road - // names to accommodate both the abbreviated names from OpenMapTiles and - // the spelled-out names from Planetiler. - // https://github.com/onthegomap/planetiler/issues/14 - "Audubon Parkway": "AU", - "Bluegrass Parkway": "BG", - "Bluegrass Pkwy": "BG", - "Cumberland Parkway": "LN", - "Cumberland Pkwy": "LN", - "Hal Rogers Parkway": "HR", - "Hal Rogers Pkwy": "HR", - "Mountain Parkway": "MP", - "Mountain Pkwy": "MP", - "Purchase Parkway": "JC", - "Purchase Pkwy": "JC", - "Western Kentucky Parkway": "WK", - "Western Kentucky Pkwy": "WK", - }; - - shields["US:CT:Parkway"].overrideByWayName = { - "Merritt Parkway": { - spriteBlank: "shield_us_ct_parkway_merritt", - }, - }; - - shields["US:MI"].overrideByRef = { - 185: diamondShield( - Color.shields.brown, - Color.shields.white, - Color.shields.white, - 0, - 24 - ), - }; - - shields["US:MO:Taney:Branson"].overrideByRef = { - "Red Route": bransonRouteShield(Color.shields.red, Color.shields.white), - "Yellow Route": bransonRouteShield( - Color.shields.yellow, - Color.shields.green - ), - "Blue Route": bransonRouteShield(Color.shields.blue, Color.shields.white), - }; - - shields["US:NY:Parkway"].refsByWayName = { - "Bear Mountain Parkway": "BMP", - "Bronx and Pelham Parkway": "PP", - "Bronx River Parkway": "BRP", - "Cross County Parkway": "CCP", - "Hutchinson River Parkway": "HRP", - "Korean War Veterans Parkway": "KWVP", - "Mosholu Parkway": "MP", - "Pelham Parkway": "PP", - "Saw Mill River Parkway": "SMP", - "Sprain Brook Parkway": "SBP", - "Taconic State Parkway": "TSP", - }; - - shields["US:PA:Allegheny:Belt"].overrideByRef = { - "Red Belt": paBeltShield(Color.shields.red, Color.shields.black), - "Orange Belt": paBeltShield(Color.shields.orange, Color.shields.black), - "Yellow Belt": paBeltShield(Color.shields.yellow, Color.shields.black), - "Green Belt": paBeltShield(Color.shields.green, Color.shields.white), - "Blue Belt": paBeltShield(Color.shields.blue, Color.shields.white), - "Purple Belt": paBeltShield(Color.shields.purple, Color.shields.white), - }; - - shields["US:TX:Fort_Bend:FBCTRA"].refsByWayName = { - "Fort Bend Parkway Toll Road": "FBP", - "Fort Bend Westpark Tollway": "WPT", - }; - shields["US:TX:Harris:HCTRA"].refsByWayName = { - "East Sam Houston Tollway North": "SHT", - "East Sam Houston Tollway South": "SHT", - "North Sam Houston Tollway East": "SHT", - "North Sam Houston Tollway West": "SHT", - "South Sam Houston Tollway East": "SHT", - "South Sam Houston Tollway West": "SHT", - "West Sam Houston Tollway North": "SHT", - "West Sam Houston Tollway South": "SHT", - "Fort Bend Toll Road": "FBTR", - "Hardy Toll Road": "HTR", - "Tomball Tollway": "TBT", - "Westpark Tollway": "WPT", - }; - - shields["GLCT"].overrideByRef = { - LECT: { - spriteBlank: "shield_glct_lect", - }, - LHCT: { - spriteBlank: "shield_glct_lhct", - }, - LMCT: { - spriteBlank: "shield_glct_lmct", - colorLighten: Color.shields.green, - }, - LSCT: { - spriteBlank: "shield_glct_lsct", - }, - }; - - shields["GLCT:Loop"].overrideByRef = { - LMCT: { - spriteBlank: "shield_glct_lmct", - colorLighten: Color.shields.brown, + return { + networks: shields, + options: { + bannerTextColor: Color.palette.black, + bannerTextHaloColor: Color.backgroundFill, + bannerHeight: 9, + bannerPadding: 1, + shieldFont: + '"Noto Sans Condensed", "Noto Sans Armenian Condensed", sans-serif-condensed, "Arial Narrow", sans-serif', + shieldSize: 20, }, }; - - return shields; } diff --git a/src/js/shield_format.ts b/src/js/shield_format.ts new file mode 100644 index 000000000..10368bbf5 --- /dev/null +++ b/src/js/shield_format.ts @@ -0,0 +1,23 @@ +import type { + StringPredicate, + RouteParser, +} from "@americana/maplibre-shield-generator"; + +import { parseImageName } from "../layer/highway_shield.js"; + +export const shieldPredicate: StringPredicate = (imageID: string) => + imageID && imageID.startsWith("shield"); + +export const networkPredicate: StringPredicate = (network: string) => + // On recreational route relations, network=* indicates the network's scope, + // not the network itself. + // https://github.com/ZeLonewolf/openstreetmap-americana/issues/94 + !/^[lrni][chimpw]n$/.test(network); + +export const routeParser: RouteParser = { + parse: (id: string) => { + return parseImageName(id); + }, + format: (network: string, ref: string, name: string) => + `shield\n${network}\n${ref}\n${name}\n`, +}; diff --git a/src/layer/boundary.js b/src/layer/boundary.js index cab85a6a6..b36d6eeec 100644 --- a/src/layer/boundary.js +++ b/src/layer/boundary.js @@ -78,6 +78,57 @@ export const county = { "source-layer": "boundary", }; +export const regionCasing = { + id: "boundary_region_casing", + type: "line", + paint: { + "line-color": Color.borderCasing, + "line-width": { + stops: [ + [8, 5], + [9, 6], + ], + }, + }, + filter: [ + "all", + ["==", ["get", "admin_level"], 5], + ["==", ["get", "disputed"], 0], + ["==", ["get", "maritime"], 0], + ], + minzoom: 8, + layout: { + "line-join": "round", + visibility: "visible", + }, + source: "openmaptiles", + "source-layer": "boundary", +}; + +export const region = { + id: "boundary_region", + type: "line", + paint: { + "line-color": Color.border, + "line-dasharray": [5, 4], + "line-width": 1, + "line-offset": 0, + }, + filter: [ + "all", + ["==", ["get", "admin_level"], 5], + ["==", ["get", "disputed"], 0], + ["==", ["get", "maritime"], 0], + ], + minzoom: 6, + layout: { + "line-join": "round", + visibility: "visible", + }, + source: "openmaptiles", + "source-layer": "boundary", +}; + export const stateCasing = { id: "boundary_state_casing", type: "line", @@ -322,6 +373,10 @@ export const legendEntries = [ description: "County or county-equivalent", layers: [county.id, countyCasing.id], }, + { + description: "Region", + layers: [region.id, regionCasing.id], + }, { description: "City, town, or village", layers: [city.id], diff --git a/src/layer/highway_shield.js b/src/layer/highway_shield.js index c431f9446..6c24abb43 100644 --- a/src/layer/highway_shield.js +++ b/src/layer/highway_shield.js @@ -1,32 +1,23 @@ "use strict"; -export const namedRouteNetworks = [ - "US:CT:Parkway", - "US:KY:Parkway", - "US:NY:Parkway", - "US:TX:Fort_Bend:FBCTRA", - "US:TX:Harris:HCTRA", -]; +const orderedRouteAttributes = ["network", "ref", "name", "color"]; export function getImageNameExpression(routeIndex) { - return [ - "concat", - "shield\n", - ["get", "route_" + routeIndex], - [ - "match", - ["get", "route_" + routeIndex], - namedRouteNetworks.map((n) => n + "="), - ["concat", "\n", ["get", "name"]], - "", - ], - ]; + let concat = ["concat", "shield"]; + for (let attr of orderedRouteAttributes) { + concat.push("\n"); + concat.push(["coalesce", ["get", `route_${routeIndex}_${attr}`], ""]); + } + return concat; } function routeConcurrency(routeIndex) { return [ "case", - ["!=", ["get", "route_" + routeIndex], null], + [ + "any", + ...orderedRouteAttributes.map((a) => ["has", `route_${routeIndex}_${a}`]), + ], ["image", getImageNameExpression(routeIndex)], ["literal", ""], ]; @@ -36,12 +27,16 @@ function routeConcurrency(routeIndex) { * Returns a structured representation of the given image name. * * @param name An image name in the format returned by `routeConcurrency`. + * @return An object with the keys in `orderedRouteAttributes` plus the full image name in `imageName`. */ export function parseImageName(imageName) { let lines = imageName.split("\n"); - let [, network, ref] = lines[1].match(/^(.*?)=(.*)/) || []; - let name = lines[2]; - return { imageName, network, ref, name }; + lines.shift(); // "shield" + let parsed = Object.fromEntries( + orderedRouteAttributes.map((a, i) => [a, lines[i]]) + ); + parsed.imageName = imageName; + return parsed; } let shieldTextField = ["format"]; @@ -112,11 +107,6 @@ export const shield = { }, filter: [ "any", - ["has", "route_1"], - ["has", "route_2"], - ["has", "route_3"], - ["has", "route_4"], - ["has", "route_5"], - ["has", "route_6"], + ...orderedRouteAttributes.map((a) => ["has", `route_1_${a}`]), ], }; diff --git a/src/layer/index.js b/src/layer/index.js index 0aa3d8ecd..740eb6061 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -37,6 +37,7 @@ export function build(locales) { lyrPark.parkFill, lyrBoundary.countyCasing, + lyrBoundary.regionCasing, lyrBoundary.stateCasing, lyrBoundary.countryCasing, @@ -52,6 +53,7 @@ export function build(locales) { lyrBoundary.city, lyrBoundary.county, + lyrBoundary.region, lyrBoundary.state, lyrBoundary.country, @@ -78,7 +80,6 @@ export function build(locales) { lyrRoad.roadTunnel.fill(), lyrOneway.tunnel, - lyrOneway.tunnelLink, lyrFerry.ferry, @@ -136,8 +137,7 @@ export function build(locales) { lyrRail.railway.fill(), - lyrOneway.road, - lyrOneway.link, + lyrOneway.surface, lyrAerialway.dragLift ); @@ -197,7 +197,6 @@ export function build(locales) { lyrRail.railwayBridge.fill(), lyrOneway.bridge, - lyrOneway.bridgeLink, ]; layers.push(...lyrRail.getLayerSeparatedBridgeLayers(bridgeLayers)); diff --git a/src/layer/oneway.js b/src/layer/oneway.js index ea8438bf9..ba681dcec 100644 --- a/src/layer/oneway.js +++ b/src/layer/oneway.js @@ -1,285 +1,101 @@ "use strict"; -export const road = { - id: "road_oneway", - type: "symbol", - paint: { - "icon-opacity": 0.5, - }, - filter: [ - "all", - ["==", ["get", "oneway"], 1], - ["!", ["in", ["get", "brunnel"], ["literal", ["bridge", "tunnel"]]]], - ["!=", ["get", "ramp"], 1], +// Common expressions +const highwaySelector = ["match", ["get", "class"]]; +const baseFilter = [ + "all", + ["==", ["get", "oneway"], 1], + ["!=", ["get", "ramp"], 1], + [ + "in", + ["get", "class"], [ - "in", - ["get", "class"], + "literal", [ - "literal", - [ - "motorway", - // "trunk", - // "primary", - // "secondary", - // "tertiary", - // "minor", - // "service", - ], + "motorway", + "trunk", + "primary", + "secondary", + "tertiary", + "busway", + "minor", + "service", ], ], ], - layout: { - "icon-size": { - stops: [ - [15, 0.5], - [19, 1], - ], - }, - "icon-image": "oneway", - visibility: "visible", - "icon-padding": 2, - "symbol-spacing": 75, - "symbol-placement": "line", - "icon-rotation-alignment": "map", - }, - source: "openmaptiles", - minzoom: 15, - "source-layer": "transportation", -}; +]; -export const tunnel = { - id: "tunnel_oneway", - type: "symbol", - paint: { - "icon-opacity": 0.5, - }, +export const surface = { + id: "oneway_surface", filter: [ - "all", - ["==", ["get", "oneway"], 1], - ["==", ["get", "brunnel"], "tunnel"], - ["!=", ["get", "ramp"], 1], - [ - "in", - ["get", "class"], - [ - "literal", - [ - "motorway", - // "trunk", - // "primary", - // "secondary", - // "tertiary", - // "minor", - // "service", - ], - ], - ], + ...baseFilter, + ["!", ["in", ["get", "brunnel"], ["literal", ["bridge", "tunnel"]]]], ], - layout: { - "icon-size": { - stops: [ - [15, 0.5], - [19, 1], - ], - }, - "icon-image": "oneway", - visibility: "visible", - //"icon-rotate": 90, - "icon-padding": 2, - "symbol-spacing": 75, - "symbol-placement": "line", - "icon-rotation-alignment": "map", - }, source: "openmaptiles", - minzoom: 15, "source-layer": "transportation", -}; - -export const bridge = { - id: "bridge_oneway", type: "symbol", - paint: { - "icon-opacity": 0.5, - }, - filter: [ - "all", - ["==", ["get", "oneway"], 1], - ["==", ["get", "brunnel"], "bridge"], - ["!=", ["get", "ramp"], 1], - [ - "in", - ["get", "class"], - [ - "literal", - [ - "motorway", - // "trunk", - // "primary", - // "secondary", - // "tertiary", - // "minor", - // "service", - ], - ], - ], - ], - layout: { - "icon-size": { - stops: [ - [15, 0.5], - [19, 1], - ], - }, - "icon-image": "oneway", - visibility: "visible", - "icon-padding": 2, - "symbol-spacing": 75, - "symbol-placement": "line", - "icon-rotation-alignment": "map", - }, - source: "openmaptiles", minzoom: 15, - "source-layer": "transportation", -}; -export const link = { - id: "road_oneway_link", - type: "symbol", - paint: { - "icon-opacity": 0.5, - }, - filter: [ - "all", - ["==", ["get", "oneway"], 1], - ["!", ["in", ["get", "brunnel"], ["literal", ["bridge", "tunnel"]]]], - ["==", ["get", "ramp"], 1], - [ - "in", - ["get", "class"], + layout: { + "icon-size": [ + "interpolate", + ["exponential", 1.2], + ["zoom"], + 15, + [...highwaySelector, ["motorway", "trunk", "primary"], 0.5, 0.3], + 19, [ - "literal", - [ - "motorway", - // "trunk", - // "primary", - // "secondary", - // "tertiary", - // "minor", - // "service", - ], + ...highwaySelector, + ["motorway", "trunk", "primary"], + 1, + "secondary", + 0.8, + 0.6, ], ], - ], - layout: { - "icon-size": { - stops: [ - [15, 0.3], - [19, 0.8], + "icon-image": [ + "match", + ["get", "brunnel"], + "tunnel", + "oneway_black", + [ + "match", + ["get", "toll"], + 1, + "oneway_black", + [...highwaySelector, "motorway", "oneway_white", "oneway_black"], ], - }, - "icon-image": "oneway", + ], visibility: "visible", "icon-padding": 2, - "symbol-spacing": 75, + "symbol-spacing": [ + "interpolate", + ["exponential", 1.2], + ["zoom"], + 15, + 75, + 19, + 300, + ], "symbol-placement": "line", "icon-rotation-alignment": "map", }, - source: "openmaptiles", - minzoom: 16, - "source-layer": "transportation", -}; - -export const tunnelLink = { - id: "tunnel_oneway_link", - type: "symbol", paint: { "icon-opacity": 0.5, }, - filter: [ - "all", - ["==", ["get", "oneway"], 1], - ["==", ["get", "brunnel"], "tunnel"], - ["==", ["get", "ramp"], 1], - [ - "in", - ["get", "class"], - [ - "literal", - [ - "motorway", - // "trunk", - // "primary", - // "secondary", - // "tertiary", - // "minor", - // "service", - ], - ], - ], - ], - layout: { - "icon-size": { - stops: [ - [15, 0.3], - [19, 0.8], - ], - }, - "icon-image": "oneway", - visibility: "visible", - //"icon-rotate": 90, - "icon-padding": 2, - "symbol-spacing": 75, - "symbol-placement": "line", - "icon-rotation-alignment": "map", - }, - source: "openmaptiles", - minzoom: 16, - "source-layer": "transportation", }; -export const bridgeLink = { - id: "bridge_oneway_link", - type: "symbol", +export const tunnel = { + ...surface, + id: "oneway_tunnel", + filter: [...baseFilter, ["==", ["get", "brunnel"], "tunnel"]], paint: { - "icon-opacity": 0.5, + "icon-opacity": 0.2, }, - filter: [ - "all", - ["==", ["get", "oneway"], 1], - ["==", ["get", "brunnel"], "bridge"], - ["==", ["get", "ramp"], 1], - [ - "in", - ["get", "class"], - [ - "literal", - [ - "motorway", - // "trunk", - // "primary", - // "secondary", - // "tertiary", - // "minor", - // "service", - ], - ], - ], - ], - layout: { - "icon-size": { - stops: [ - [15, 0.3], - [19, 0.8], - ], - }, - "icon-image": "oneway", - visibility: "visible", - "icon-padding": 2, - "symbol-spacing": 75, - "symbol-placement": "line", - "icon-rotation-alignment": "map", - }, - source: "openmaptiles", - minzoom: 16, - "source-layer": "transportation", +}; + +export const bridge = { + ...surface, + id: "oneway_bridge", + filter: [...baseFilter, ["==", ["get", "brunnel"], "bridge"]], }; diff --git a/src/layer/park.js b/src/layer/park.js index 3cb7f1b4c..bcf2cebd3 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -3,8 +3,11 @@ import * as Label from "../constants/label.js"; import * as Color from "../constants/color.js"; +const parkLayerFilter = ["!=", ["get", "class"], "aboriginal_lands"]; + export const fill = { id: "protected-area_fill", + filter: parkLayerFilter, type: "fill", paint: { "fill-color": Color.parkFill, @@ -15,6 +18,7 @@ export const fill = { export const outline = { id: "protected-area_outline", + filter: parkLayerFilter, type: "line", paint: { "line-color": Color.parkOutline, @@ -27,7 +31,7 @@ export const outline = { export const label = { id: "protected-area_label", type: "symbol", - filter: ["has", "rank"], + filter: ["all", ["has", "rank"], parkLayerFilter], paint: { "text-color": Color.parkLabel, "text-halo-blur": 1, diff --git a/src/layer/place.js b/src/layer/place.js index 5335bbfb2..611199a7d 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -20,39 +20,45 @@ const cityLabelPaint = { "text-halo-blur": labelHaloBlur, }; -const cityIcon = [ +const minorLocationStepFilter = [ + "step", + ["zoom"], + ["<=", ["get", "rank"], 2], + 6, + ["<=", ["get", "rank"], 4], + 7, + ["<=", ["get", "rank"], 5], + 8, + ["<=", ["get", "rank"], 9], + 10, + [">=", ["get", "rank"], 1], +]; + +const iconImage = [ "match", ["get", "capital"], 2, - "star_nation_capital", + "place_star_in_circle", 3, - "star_state_capital", + "place_star", 4, - "star_state_capital", - "dot_city", + "place_star", + 5, + "place_dot_in_circle", + 6, + "place_dot_in_circle", + "place_dot", ]; +function filterPlace(type) { + return ["==", ["get", "class"], type]; +} + export const village = { id: "place_village", type: "symbol", paint: cityLabelPaint, - filter: [ - "all", - ["==", ["get", "class"], "village"], - [ - "step", - ["zoom"], - ["<=", ["get", "rank"], 2], - 6, - ["<=", ["get", "rank"], 4], - 7, - ["<=", ["get", "rank"], 5], - 8, - ["<=", ["get", "rank"], 9], - 10, - [">=", ["get", "rank"], 1], - ], - ], + filter: ["all", filterPlace("village"), minorLocationStepFilter], layout: { "text-font": ["Americana-Bold"], "text-size": { @@ -63,7 +69,7 @@ export const village = { [12, 12], ], }, - "icon-image": cityIcon, + "icon-image": iconImage, "icon-size": { base: 1.0, stops: [ @@ -99,23 +105,7 @@ export const town = { id: "place_town", type: "symbol", paint: cityLabelPaint, - filter: [ - "all", - ["==", ["get", "class"], "town"], - [ - "step", - ["zoom"], - ["<=", ["get", "rank"], 2], - 6, - ["<=", ["get", "rank"], 4], - 7, - ["<=", ["get", "rank"], 5], - 8, - ["<=", ["get", "rank"], 9], - 10, - [">=", ["get", "rank"], 1], - ], - ], + filter: ["all", filterPlace("town"), minorLocationStepFilter], layout: { "text-font": ["Americana-Bold"], "text-size": { @@ -126,7 +116,7 @@ export const town = { [12, 18], ], }, - "icon-image": cityIcon, + "icon-image": iconImage, "icon-size": { base: 1.2, stops: [ @@ -164,7 +154,7 @@ export const city = { paint: cityLabelPaint, filter: [ "all", - ["==", ["get", "class"], "city"], + filterPlace("city"), [ "step", ["zoom"], @@ -185,7 +175,7 @@ export const city = { [11, 24], ], }, - "icon-image": cityIcon, + "icon-image": iconImage, "icon-size": { base: 1.2, stops: [ @@ -467,4 +457,14 @@ export const legendEntries = [ layers: populatedPlaceLayers, filter: ["==", ["get", "capital"], 4], }, + { + description: "Local capital", + layers: populatedPlaceLayers, + filter: ["==", ["get", "capital"], 5], + }, + { + description: "County seat or equivalent", + layers: populatedPlaceLayers, + filter: ["==", ["get", "capital"], 6], + }, ]; diff --git a/src/layer/poi.js b/src/layer/poi.js index 47437f3e2..d9a5ddbcc 100644 --- a/src/layer/poi.js +++ b/src/layer/poi.js @@ -19,6 +19,14 @@ var iconDefs = { color: Color.poi.consumer, description: "Bar or pub", }, + bookstore: { + classes: { + library: ["books"], + }, + sprite: "poi_book_upright", + color: Color.poi.consumer, + description: "Bookstore", + }, bus_station: { classes: { bus: ["bus_station"], @@ -35,6 +43,30 @@ var iconDefs = { color: Color.poi.transport, description: "Bus stop", }, + car_repair: { + classes: { + car: ["car_repair"], + }, + sprite: "poi_car_repair", + color: Color.poi.consumer, + description: "Car mechanic", + }, + car_shop: { + classes: { + car: ["car"], + }, + sprite: "poi_car_shop", + color: Color.poi.consumer, + description: "Car dealership", + }, + taxi: { + classes: { + office: ["taxi"], + }, + sprite: "poi_taxi", + color: Color.poi.transport, + description: "Taxi stand", + }, coffee: { classes: { cafe: ["cafe"], @@ -43,6 +75,14 @@ var iconDefs = { color: Color.poi.consumer, description: "Coffee shop", }, + fuel: { + classes: { + fuel: ["fuel"], + }, + sprite: "poi_fuel", + color: Color.poi.consumer, + description: "Gas station", + }, hospital: { classes: { hospital: ["hospital"], @@ -51,6 +91,30 @@ var iconDefs = { color: Color.poi.infrastructure, description: "Hospital", }, + hotel: { + classes: { + lodging: ["hotel", "motel", "guest_house"], + }, + sprite: "poi_hotel", + color: Color.poi.consumer, + description: "Hotel", + }, + hostel: { + classes: { + lodging: ["hostel"], + }, + sprite: "poi_hostel", + color: Color.poi.consumer, + description: "Hostel", + }, + library: { + classes: { + library: ["library"], + }, + sprite: "poi_book_upright", + color: Color.poi.infrastructure, + description: "Library", + }, medical: { classes: { hospital: ["clinic"], @@ -76,6 +140,86 @@ var iconDefs = { color: Color.poi.infrastructure, description: "Parking", }, + police: { + classes: { + police: ["police"], + }, + sprite: "poi_police_shield", + color: Color.poi.infrastructure, + description: "Police station", + }, + post_office: { + classes: { + post: ["post_office"], + }, + sprite: "poi_envelope", + color: Color.poi.infrastructure, + description: "Post office", + }, + pow_buddhist: { + classes: { + place_of_worship: ["buddhist"], + }, + sprite: "poi_pow_buddhist", + color: Color.poi.infrastructure, + description: "Buddhist place of worship", + }, + pow_christian: { + classes: { + place_of_worship: ["christian"], + }, + sprite: "poi_pow_christian", + color: Color.poi.infrastructure, + description: "Christian place of worship", + }, + pow_hindu: { + classes: { + place_of_worship: ["hindu"], + }, + sprite: "poi_pow_hindu", + color: Color.poi.infrastructure, + description: "Hindu place of worship", + }, + pow_jewish: { + classes: { + place_of_worship: ["jewish"], + }, + sprite: "poi_pow_jewish", + color: Color.poi.infrastructure, + description: "Jewish place of worship", + }, + pow_muslim: { + classes: { + place_of_worship: ["muslim"], + }, + sprite: "poi_pow_muslim", + color: Color.poi.infrastructure, + description: "Muslim place of worship", + }, + pow_sikh: { + classes: { + place_of_worship: ["sikh"], + }, + sprite: "poi_pow_sikh", + color: Color.poi.infrastructure, + description: "Sikh place of worship", + }, + pow_shinto: { + classes: { + place_of_worship: ["shinto"], + }, + sprite: "poi_pow_shinto", + color: Color.poi.infrastructure, + description: "Shinto place of worship", + }, + pow_taoist: { + classes: { + place_of_worship: ["taoist"], + }, + sprite: "poi_pow_taoist", + color: Color.poi.infrastructure, + description: "Taoist place of worship", + }, railway_station: { classes: { railway: ["station", "halt", "subway"], @@ -95,12 +239,27 @@ var iconDefs = { school: { classes: { school: ["kindergarten", "school"], - college: ["college", "university"], }, sprite: "poi_school", color: Color.poi.infrastructure, description: "School", }, + supermarket: { + classes: { + grocery: ["supermarket"], + }, + sprite: "poi_supermarket", + color: Color.poi.consumer, + description: "Supermarket", + }, + college: { + classes: { + college: ["college", "university"], + }, + sprite: "poi_mortarboard", + color: Color.poi.infrastructure, + description: "College or university", + }, townhall: { classes: { town_hall: ["townhall"], @@ -159,18 +318,46 @@ export const poi = { "text-color": [ "match", ["get", "subclass"], - [...getSubclasses(iconDefs.bar), ...getSubclasses(iconDefs.coffee)], + [ + ...getSubclasses(iconDefs.fuel), + ...getSubclasses(iconDefs.bar), + ...getSubclasses(iconDefs.bookstore), + ...getSubclasses(iconDefs.coffee), + ...getSubclasses(iconDefs.supermarket), + ...getSubclasses(iconDefs.car_shop), + ...getSubclasses(iconDefs.car_repair), + ...getSubclasses(iconDefs.hotel), + ...getSubclasses(iconDefs.hostel), + ], Color.poi.consumer, [ "bus_station", "bus_stop", ...getSubclasses(iconDefs.railway_station), ...getSubclasses(iconDefs.railway_stop), + ...getSubclasses(iconDefs.taxi), ], Color.poi.transport, ["museum"], Color.poi.attraction, - ["hospital", "parking", "school", "townhall"], + [ + "hospital", + "parking", + "police", + "school", + ...getSubclasses(iconDefs.college), + "library", + "townhall", + ...getSubclasses(iconDefs.post_office), + ...getSubclasses(iconDefs.pow_christian), + ...getSubclasses(iconDefs.pow_buddhist), + ...getSubclasses(iconDefs.pow_hindu), + ...getSubclasses(iconDefs.pow_jewish), + ...getSubclasses(iconDefs.pow_muslim), + ...getSubclasses(iconDefs.pow_sikh), + ...getSubclasses(iconDefs.pow_shinto), + ...getSubclasses(iconDefs.pow_taoist), + ], Color.poi.infrastructure, Color.poi.infrastructure, ], @@ -181,6 +368,8 @@ export const poi = { [ "match", ["get", "subclass"], + [...getSubclasses(iconDefs.college)], + 10, ["station", "halt"], 12, ["bus_station", "subway"], @@ -188,13 +377,35 @@ export const poi = { [ "bus_stop", "hospital", + "library", "museum", + "police", + ...getSubclasses(iconDefs.fuel), + ...getSubclasses(iconDefs.post_office), + ...getSubclasses(iconDefs.pow_buddhist), + ...getSubclasses(iconDefs.pow_christian), + ...getSubclasses(iconDefs.pow_hindu), + ...getSubclasses(iconDefs.pow_jewish), + ...getSubclasses(iconDefs.pow_muslim), + ...getSubclasses(iconDefs.pow_sikh), + ...getSubclasses(iconDefs.pow_shinto), + ...getSubclasses(iconDefs.pow_taoist), ...getSubclasses(iconDefs.school), + ...getSubclasses(iconDefs.supermarket), "townhall", "tram_stop", ], 15, - [...getSubclasses(iconDefs.bar), ...getSubclasses(iconDefs.coffee)], + [ + ...getSubclasses(iconDefs.bar), + ...getSubclasses(iconDefs.bookstore), + ...getSubclasses(iconDefs.coffee), + ...getSubclasses(iconDefs.car_shop), + ...getSubclasses(iconDefs.car_repair), + ...getSubclasses(iconDefs.taxi), + ...getSubclasses(iconDefs.hotel), + ...getSubclasses(iconDefs.hostel), + ], 16, ["clinic", "doctors", "parking"], 17, @@ -219,7 +430,7 @@ export const poi = { [ "match", ["get", "subclass"], - ["bus_stop", "tram_stop"], + ["bus_stop", "tram_stop", "fuel", "supermarket"], "", label.localizedName, ], diff --git a/src/layer/road_label.js b/src/layer/road_label.js index 332d88ebe..c820b8631 100644 --- a/src/layer/road_label.js +++ b/src/layer/road_label.js @@ -157,7 +157,7 @@ export const bridgeSpacer = { layout: { "symbol-placement": "line", "symbol-spacing": 2, - "icon-image": "dot_city", + "icon-image": "place_dot", "icon-allow-overlap": true, "icon-size": 0.1, }, diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index 5ea963582..45b3f30f3 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -176,6 +176,7 @@ export const bridgeSpacer = { "all", ["==", ["get", "brunnel"], "bridge"], ["in", ["geometry-type"], ["literal", ["LineString"]]], + ["!=", ["get", "oneway"], 1], ], paint: { "icon-opacity": 0, @@ -183,7 +184,7 @@ export const bridgeSpacer = { layout: { "symbol-placement": "line", "symbol-spacing": 2, - "icon-image": "dot_city", + "icon-image": "place_dot", "icon-allow-overlap": true, "icon-size": 0.1, }, diff --git a/src/search.js b/src/search.js index bb59ec1ca..3639a505b 100644 --- a/src/search.js +++ b/src/search.js @@ -202,6 +202,11 @@ function arrowNavigate(e) { e.preventDefault(); break; case "Enter": + if (liveResults.children.length === 0) { + e.preventDefault(); + return; // Do nothing if no results + } + let navIndex = resultSelectIndex < 0 ? 0 : resultSelectIndex; goToResult(navIndex); diff --git a/src/shieldtest.html b/src/shieldtest.html index 911c5d118..164891c8e 100644 --- a/src/shieldtest.html +++ b/src/shieldtest.html @@ -3,16 +3,8 @@ Shield Test - - - - + +