├── .commitlintrc.cjs ├── .eslintignore ├── .eslintrc.cjs ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md └── workflows │ ├── commitlint.yml │ ├── notify-discord.yml │ ├── on-publish.yml │ ├── on-pull-request.yml │ ├── on-push-main.yml │ └── release.yml ├── .gitignore ├── .nvmrc ├── .releaserc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.cjs ├── examples ├── expressions.ts ├── filter.ts ├── parsersample.ts ├── rastersample.ts ├── sample.ts └── typeguardsample.ts ├── functions.ts ├── index.ts ├── jest.config.cjs ├── package-lock.json ├── package.json ├── renovate.json ├── schema.json ├── style.ts ├── test └── typeguards.spec.ts ├── tsconfig.json └── typeguards.ts /.commitlintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | }; 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | dist 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: '@terrestris/eslint-config-typescript', 3 | rules: { 4 | camelcase: [ 5 | 'off', 6 | { 7 | ignoreImports: true 8 | } 9 | ] 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository#about-funding-files 2 | open_collective: geostyler 3 | 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: jansule, KaiVolland 7 | 8 | --- 9 | 10 | # Bug 11 | 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Desktop (please complete the following information):** 29 | - OS: [e.g. iOS] 30 | - Browser [e.g. chrome, safari] 31 | - Version [e.g. 22] 32 | 33 | **Smartphone (please complete the following information):** 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: jansule, KaiVolland 7 | 8 | --- 9 | 10 | # Feature Request 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question related to this project 4 | title: '' 5 | labels: question 6 | assignees: jansule, KaiVolland 7 | 8 | --- 9 | 10 | # Question 11 | 12 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request, push] 3 | 4 | jobs: 5 | commitlint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | with: 10 | fetch-depth: 0 11 | - uses: wagoid/commitlint-github-action@v6 12 | with: 13 | configFile: .commitlintrc.cjs 14 | -------------------------------------------------------------------------------- /.github/workflows/notify-discord.yml: -------------------------------------------------------------------------------- 1 | name: Discord notification 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | discord-notification: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Discord notification 📯 12 | env: 13 | DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} 14 | uses: Ilshidur/action-discord@0.3.2 15 | with: 16 | args: '${{ github.event.repository.name }} [${{ github.event.release.tag_name }}](${{ github.event.release.html_url }}) has been released. 🚀' 17 | -------------------------------------------------------------------------------- /.github/workflows/on-publish.yml: -------------------------------------------------------------------------------- 1 | name: Update gh-pages 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | build_docs: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout sources 13 | uses: actions/checkout@v4 14 | with: 15 | persist-credentials: false 16 | 17 | - name: Use Node.js 20.x 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: 20.x 21 | 22 | - name: Cache Node.js modules 💾 23 | uses: actions/cache@v4 24 | with: 25 | path: ~/.npm 26 | key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} 27 | restore-keys: | 28 | ${{ runner.OS }}-node- 29 | ${{ runner.OS }}- 30 | 31 | - name: Install dependencies ⏬ 32 | run: npm install 33 | 34 | - name: Generate documentation 🏗️ 35 | run: npm run generate-docs 36 | 37 | - name: Get geostyler version 38 | run: | 39 | echo "VERSION=$(node -pe "require('./package.json').version")" >> $GITHUB_ENV 40 | 41 | - name: Deploy (to docs/v${{ env.VERSION }}) 🚀 42 | uses: JamesIves/github-pages-deploy-action@v4.6.1 43 | with: 44 | branch: gh-pages 45 | folder: docs 46 | target-folder: docs/v${{ env.VERSION }} 47 | 48 | - name: Deploy (to docs/latest) 🚀 49 | uses: JamesIves/github-pages-deploy-action@v4.6.1 50 | with: 51 | branch: gh-pages 52 | folder: docs 53 | target-folder: docs/latest 54 | -------------------------------------------------------------------------------- /.github/workflows/on-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Test build of geostyler-style 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [20.x, 22.x] 12 | 13 | steps: 14 | - name: Checkout sources 15 | uses: actions/checkout@v4 16 | 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | 22 | - name: Cache Node.js modules 💾 23 | uses: actions/cache@v4 24 | with: 25 | path: ~/.npm 26 | key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} 27 | restore-keys: | 28 | ${{ runner.OS }}-node- 29 | ${{ runner.OS }}- 30 | 31 | - name: Install dependencies ⏬ 32 | run: npm install 33 | 34 | - name: Lint code 💄 35 | run: npm run lint 36 | 37 | - name: Test code ✅ 38 | run: npm run test 39 | 40 | - name: Build artifacts 🏗️ 41 | run: npm run build 42 | -------------------------------------------------------------------------------- /.github/workflows/on-push-main.yml: -------------------------------------------------------------------------------- 1 | name: Lint and generate-docs for geostyler-style 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@v4 15 | 16 | - name: Use Node.js 20.x 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: 20.x 20 | 21 | - name: Cache Node.js modules 💾 22 | uses: actions/cache@v4 23 | with: 24 | path: ~/.npm 25 | key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} 26 | restore-keys: | 27 | ${{ runner.OS }}-node- 28 | ${{ runner.OS }}- 29 | 30 | - name: Install dependencies ⏬ 31 | run: npm install 32 | 33 | - name: Lint code 💄 34 | run: npm run lint 35 | 36 | - name: Test code ✅ 37 | run: npm run test 38 | 39 | build_docs: 40 | runs-on: ubuntu-latest 41 | 42 | steps: 43 | - name: Checkout sources 44 | uses: actions/checkout@v4 45 | 46 | - name: Use Node.js 20.x 47 | uses: actions/setup-node@v4 48 | with: 49 | node-version: 20.x 50 | 51 | - name: Cache Node.js modules 💾 52 | uses: actions/cache@v4 53 | with: 54 | path: ~/.npm 55 | key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} 56 | restore-keys: | 57 | ${{ runner.OS }}-node- 58 | ${{ runner.OS }}- 59 | 60 | - name: Install dependencies ⏬ 61 | run: npm install 62 | 63 | - name: Generate documentation 🏗️ 64 | run: npm run generate-docs 65 | 66 | - name: Deploy (to docs/main) 🚀 67 | uses: JamesIves/github-pages-deploy-action@v4.6.1 68 | with: 69 | branch: gh-pages 70 | folder: docs 71 | target-folder: docs/main 72 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - next 8 | 9 | jobs: 10 | release: 11 | name: Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout sources 🔰 15 | uses: actions/checkout@v4 16 | 17 | - name: Setup Node.js 20 👷🏻 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: 20 21 | 22 | - name: Install dependencies ⏬ 23 | run: npm ci 24 | 25 | - name: Build artifacts 🏗️ 26 | run: npm run build 27 | 28 | - name: Release 🚀 29 | uses: cycjimmy/semantic-release-action@v4.1.0 30 | id: semantic 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }} 33 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # misc 7 | npm-debug.log* 8 | *.log 9 | 10 | # dist 11 | dist 12 | 13 | # docs 14 | docs 15 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.15.0 2 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | { 5 | "name": "next", 6 | "prerelease": true 7 | } 8 | ], 9 | "plugins": [ 10 | [ 11 | "@semantic-release/commit-analyzer", 12 | { 13 | "preset": "conventionalcommits" 14 | } 15 | ], 16 | [ 17 | "@semantic-release/release-notes-generator", 18 | { 19 | "preset": "conventionalcommits", 20 | "presetConfig": { 21 | "header": "Changelog of GeoStyler Style" 22 | } 23 | } 24 | ], 25 | "@semantic-release/changelog", 26 | "@semantic-release/npm", 27 | [ 28 | "@semantic-release/git", 29 | { 30 | "assets": [ 31 | "CHANGELOG.md", "package.json", "package-lock.json" 32 | ], 33 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 34 | } 35 | ], 36 | "@semantic-release/github" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [10.0.0](https://github.com/geostyler/geostyler-style/compare/v9.2.0...v10.0.0) (2025-02-05) 2 | 3 | 4 | ### ⚠ BREAKING CHANGES 5 | 6 | * Due to the added WellKnownNames, typechecks for distinct values of WellKnownNames need to be adjusted to also distinguish the newly added WellKnownNames. 7 | 8 | ### Features 9 | 10 | * add WellKnownNames to support extra styles from QGIS ([#652](https://github.com/geostyler/geostyler-style/issues/652)) ([1e9e90b](https://github.com/geostyler/geostyler-style/commit/1e9e90be9fd11b710772cd5a77334fb887df4fee)) 11 | 12 | ## [9.2.0](https://github.com/geostyler/geostyler-style/compare/v9.1.0...v9.2.0) (2024-11-26) 13 | 14 | 15 | ### Features 16 | 17 | * add graphicfillpadding on fill symbolizer ([711b71b](https://github.com/geostyler/geostyler-style/commit/711b71ba31e822bf8e72b733d2aad1fcef3f7d95)) 18 | 19 | ## [9.1.0](https://github.com/geostyler/geostyler-style/compare/v9.0.1...v9.1.0) (2024-07-09) 20 | 21 | 22 | ### Features 23 | 24 | * allow numbers in the in function ([2907811](https://github.com/geostyler/geostyler-style/commit/2907811f4d0c3efe61650d816406c6b7a57bbe9a)) 25 | 26 | ## [9.0.1](https://github.com/geostyler/geostyler-style/compare/v9.0.0...v9.0.1) (2024-06-25) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * fix elintrc name ([07af12e](https://github.com/geostyler/geostyler-style/commit/07af12e65b7840820c440442abad989e813a5431)) 32 | 33 | ## [9.0.0](https://github.com/geostyler/geostyler-style/compare/v8.1.0...v9.0.0) (2024-06-25) 34 | 35 | 36 | ### ⚠ BREAKING CHANGES 37 | 38 | * order of arguments of the FCase function has been change 39 | 40 | ### Features 41 | 42 | * add interpolate function ([ac4a927](https://github.com/geostyler/geostyler-style/commit/ac4a9270b70a2d388a3954f2e7d6e6c94ea120eb)) 43 | * add new functions ([b15e97f](https://github.com/geostyler/geostyler-style/commit/b15e97f91210f989725b92ed7b0d8bf3bed51ea3)) 44 | * export case and step params ([ea7cbaf](https://github.com/geostyler/geostyler-style/commit/ea7cbaf42dbc91495d0fa4b8fa7ca8f11d60dcae)) 45 | * prepare pre releases ([3290ff1](https://github.com/geostyler/geostyler-style/commit/3290ff124d5e5d3d69345ee4aca83d3d66fd45e2)) 46 | 47 | 48 | ### Bug Fixes 49 | 50 | * **deps:** update dependency @types/lodash to v4.17.5 ([d32fec9](https://github.com/geostyler/geostyler-style/commit/d32fec9d3b396349f501a19660fe1bbdb9b5a515)) 51 | * fix semantic release plugin ([944a92f](https://github.com/geostyler/geostyler-style/commit/944a92faa85cdafd384ed732aafef3df870969a0)) 52 | * fix tsconfig ([a49482e](https://github.com/geostyler/geostyler-style/commit/a49482ea49842f039ce3bbd189e894090e14a106)) 53 | * switch to node 20 ([89c4f64](https://github.com/geostyler/geostyler-style/commit/89c4f64661976200cc38aa85462fed5e5025e495)) 54 | * update FCase arguments ([2d51319](https://github.com/geostyler/geostyler-style/commit/2d513191a62b58a841c695c3ba0e3fd0426d9dcb)) 55 | * update repo schema ([0b4c5c1](https://github.com/geostyler/geostyler-style/commit/0b4c5c144dc72c0591327478152a1a92c1e23a29)) 56 | * use proper node version ([8b72f62](https://github.com/geostyler/geostyler-style/commit/8b72f62fb9597faa4ade9242afbb8a1075822d6f)) 57 | * use single entry point ([12d2d3b](https://github.com/geostyler/geostyler-style/commit/12d2d3b9b1ebd3d683306b932b29339bb8bc9049)) 58 | 59 | ## [9.0.0-next.5](https://github.com/geostyler/geostyler-style/compare/v9.0.0-next.4...v9.0.0-next.5) (2024-06-20) 60 | 61 | 62 | ### Bug Fixes 63 | 64 | * use single entry point ([8a918ce](https://github.com/geostyler/geostyler-style/commit/8a918ce2a59c00d2206e5b818568f402ace13a57)) 65 | 66 | ## [9.0.0-next.4](https://github.com/geostyler/geostyler-style/compare/v9.0.0-next.3...v9.0.0-next.4) (2024-06-19) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * switch to node 20 ([dd9979f](https://github.com/geostyler/geostyler-style/commit/dd9979f32960c146520f8a380caaa99bc2d4416e)) 72 | 73 | ## [9.0.0-next.3](https://github.com/geostyler/geostyler-style/compare/v9.0.0-next.2...v9.0.0-next.3) (2024-06-19) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * use proper node version ([993a896](https://github.com/geostyler/geostyler-style/commit/993a896d84687a54c0e5f9f8b1329940cde46165)) 79 | 80 | ## [9.0.0-next.2](https://github.com/geostyler/geostyler-style/compare/v9.0.0-next.1...v9.0.0-next.2) (2024-06-19) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * fix tsconfig ([37973c5](https://github.com/geostyler/geostyler-style/commit/37973c56bdedcade31e213ef3a33345a2a3ce27c)) 86 | 87 | ## [9.0.0-next.1](https://github.com/geostyler/geostyler-style/compare/v8.1.0...v9.0.0-next.1) (2024-06-18) 88 | 89 | 90 | ### ⚠ BREAKING CHANGES 91 | 92 | * order of arguments of the FCase function has been change 93 | 94 | ### Features 95 | 96 | * add new functions ([b15e97f](https://github.com/geostyler/geostyler-style/commit/b15e97f91210f989725b92ed7b0d8bf3bed51ea3)) 97 | * export case and step params ([ea7cbaf](https://github.com/geostyler/geostyler-style/commit/ea7cbaf42dbc91495d0fa4b8fa7ca8f11d60dcae)) 98 | * prepare pre releases ([0f1f859](https://github.com/geostyler/geostyler-style/commit/0f1f8595e60897e1c0ced88406424f9a75fb2431)) 99 | * produce esm build ([3b71147](https://github.com/geostyler/geostyler-style/commit/3b71147905265b9f96b6856ab61bc5962a32f532)) 100 | 101 | 102 | ### Bug Fixes 103 | 104 | * **deps:** update dependency @types/lodash to v4.17.5 ([d32fec9](https://github.com/geostyler/geostyler-style/commit/d32fec9d3b396349f501a19660fe1bbdb9b5a515)) 105 | * fix esm exports ([bfba56b](https://github.com/geostyler/geostyler-style/commit/bfba56b965e6eaf1c9f55b167ec1e0d39e9bcb6b)) 106 | * fix semantic release plugin ([0954bd7](https://github.com/geostyler/geostyler-style/commit/0954bd7a0194a125436aa55a9cf92103e2983632)) 107 | * update FCase arguments ([2d51319](https://github.com/geostyler/geostyler-style/commit/2d513191a62b58a841c695c3ba0e3fd0426d9dcb)) 108 | * update repo schema ([0b4c5c1](https://github.com/geostyler/geostyler-style/commit/0b4c5c144dc72c0591327478152a1a92c1e23a29)) 109 | 110 | ## [8.1.0](https://github.com/geostyler/geostyler-style/compare/v8.0.0...v8.1.0) (2023-12-01) 111 | 112 | 113 | ### Features 114 | 115 | * adds isSprite typeguard ([2684c38](https://github.com/geostyler/geostyler-style/commit/2684c3836dd90658de79dbd5d552142321bad389)) 116 | 117 | ## [8.0.0](https://github.com/geostyler/geostyler-style/compare/v7.5.0...v8.0.0) (2023-11-30) 118 | 119 | 120 | ### ⚠ BREAKING CHANGES 121 | 122 | * IconSymbolizer.image can be a Sprite 123 | 124 | ### Features 125 | 126 | * add support for image sprites ([f51c297](https://github.com/geostyler/geostyler-style/commit/f51c29760ab0536b50edbeab648785fd72f43bc2)) 127 | 128 | 129 | ### Bug Fixes 130 | 131 | * replace width and height with size ([f264c83](https://github.com/geostyler/geostyler-style/commit/f264c830cfd213a6c98aba9314b462d8452ef573)) 132 | * add missing semantic-release dependency ([aa60480](https://github.com/geostyler/geostyler-style/commit/aa6048023b89ba0f8f217ead4b0c542c375c65ec)) 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright © 2018-present, terrestris GmbH & Co. KG and GeoStyler contributors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geostyler-style 2 | 3 | [![Lint & Docs (main)](https://github.com/geostyler/geostyler-style/actions/workflows/on-push-main.yml/badge.svg)](https://github.com/geostyler/geostyler-style/actions/workflows/on-push-main.yml) 4 | [![Create & publish versioned documentation](https://github.com/geostyler/geostyler-style/actions/workflows/on-publish.yml/badge.svg)](https://github.com/geostyler/geostyler-style/actions/workflows/on-publish.yml) 5 | [![License](https://img.shields.io/github/license/geostyler/geostyler-style)](https://github.com/geostyler/geostyler-style/blob/main/LICENSE) 6 | 7 | TypeScript Declaration File for the [GeoStyler](https://github.com/geostyler/geostyler) Style. 8 | 9 | ## :rocket: GeoStyler Code Sprint 2025 10 | 11 | We are happy to announce the next GeoStyler Code Sprint from **02.-06.06.2025** in Switzerland. Be part of it! More infos on https://geostyler.org/. 12 | 13 | ### Documentation 14 | 15 | https://geostyler.github.io/geostyler-style/docs/main/ 16 | 17 | ### Why another style format? 18 | 19 | The *Geostyler Style* is not primarily intended to be a new standard for styling geographic data. 20 | 21 | The main task of the format is to be an **exchange format** for all the other standards. It should be 22 | capable of storing the styling possibilities of SLD, OpenLayers-Styles, MapBox-Styles, CartoCSS (and others) 23 | while being easily read- and writable. 24 | 25 | On the other hand it is used by the [GeoStyler](https://github.com/geostyler/geostyler) to build the UI components. This 26 | way you should be able to configure any type of style via UI and also translate between all supported formats (in theory :grin:). 27 | 28 | ### TypeGuards 29 | 30 | In addition to the definition itself, there are some custom [TypeGuards](https://basarat.gitbook.io/typescript/type-system/typeguard#user-defined-type-guards) that can be used as utility methods to enhance the coding experience with the geostyler-style. 31 | 32 | ```typescript 33 | import { isFilter, isSymbolizer } from 'geostyler-style/dist/typeguards'; 34 | ``` 35 | 36 | ## Funding & financial sponsorship 37 | 38 | Maintenance and further development of this code can be funded through the 39 | [GeoStyler Open Collective](https://opencollective.com/geostyler). All contributions and 40 | expenses can transparently be reviewed by anyone; you see what we use the donated money for. 41 | Thank you for any financial support you give the GeoStyler project 💞 42 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@babel/env', 4 | '@babel/preset-typescript' 5 | ], 6 | plugins: [ 7 | '@babel/proposal-object-rest-spread' 8 | ] 9 | }; 10 | -------------------------------------------------------------------------------- /examples/expressions.ts: -------------------------------------------------------------------------------- 1 | import { Style } from '../index'; 2 | 3 | const sampleExpressionStyle: Style = { 4 | name: 'Sample style using expressions', 5 | rules: [ 6 | { 7 | name: 'The max of Pi ', 8 | symbolizers: [{ 9 | kind: 'Text', 10 | lineHeight: { 11 | name: 'max', 12 | args: [{ 13 | name: 'strLength', 14 | args: [{ 15 | name: 'strConcat', 16 | args: ['pr', { 17 | name: 'property', 18 | args: ['Hilde'] 19 | }] 20 | }] 21 | }, { 22 | name: 'pi' 23 | }] 24 | } 25 | }] 26 | } 27 | ] 28 | }; 29 | 30 | export default sampleExpressionStyle; 31 | -------------------------------------------------------------------------------- /examples/filter.ts: -------------------------------------------------------------------------------- 1 | import { Style } from '../index'; 2 | 3 | const pointSimplePoint: Style = { 4 | name: 'Simple Point Filter', 5 | rules: [{ 6 | filter: ['&&', 7 | ['==', 'NAME', 'New York'], 8 | ['==', 'TEST_BOOL', 'true'], 9 | ['==', 'TEST', null], 10 | ['*=', 'TEST2', '*York*'], 11 | ['*=', 'TEST1', '*New*'], 12 | ['!', ['>', 'POPULATION', '100000']], 13 | ['||', 14 | ['==', 'TEST2', '1'], 15 | ['==', 'TEST2', '2'] 16 | ], 17 | ['<=x<=', 'TEST3', 1, 5] 18 | ], 19 | name: 'Small populated New Yorks', 20 | scaleDenominator: { 21 | max: 20000, 22 | min: 10000 23 | }, 24 | symbolizers: [{ 25 | kind: 'Mark', 26 | wellKnownName: 'circle', 27 | color: '#FF0000', 28 | radius: 3, 29 | strokeColor: '#000000', 30 | strokeWidth: 2 31 | }] 32 | }] 33 | }; 34 | 35 | export default pointSimplePoint; 36 | -------------------------------------------------------------------------------- /examples/parsersample.ts: -------------------------------------------------------------------------------- 1 | import { Style, StyleParser, UnsupportedProperties } from '../index'; 2 | import { ReadStyleResult, WriteStyleResult } from '../style'; 3 | 4 | export class SampleParser implements StyleParser { 5 | 6 | public static unsupportedProperties: UnsupportedProperties = { 7 | ScaleDenominator: 'partial', 8 | Symbolizer: { 9 | IconSymbolizer: { 10 | color: 'partial' 11 | }, 12 | LineSymbolizer: { 13 | cap: 'none', 14 | blur: { 15 | support: 'partial', 16 | info: 'Blur is only partially supported' 17 | } 18 | } 19 | }, 20 | Function: { 21 | acos: 'none', 22 | strAbbreviate: { 23 | support: 'partial', 24 | info: 'Better use strSubstring instead.' 25 | } 26 | } 27 | }; 28 | 29 | title = 'Sample Parser'; 30 | 31 | writeStyle(geoStylerStyle: Style): Promise { 32 | return new Promise(() => { 33 | return { 34 | output: 'sample' 35 | }; 36 | }); 37 | } 38 | 39 | readStyle(sldString: string): Promise { 40 | return new Promise(() => { 41 | return { 42 | output: { 43 | name: 'Samplestyle', 44 | rules: [] 45 | } 46 | }; 47 | }); 48 | } 49 | }; 50 | 51 | export default SampleParser; 52 | -------------------------------------------------------------------------------- /examples/rastersample.ts: -------------------------------------------------------------------------------- 1 | import { Style } from '../index'; 2 | 3 | const sampleRasterStyle: Style = { 4 | name: 'Sample Raster Style', 5 | rules: [ 6 | { 7 | name: 'Very old Peter', 8 | filter: ['&&', 9 | ['==', 'name', 'Peter'], 10 | ['>=', 'age', 51] 11 | ], 12 | scaleDenominator: { 13 | min: 500, 14 | max: 1000 15 | }, 16 | symbolizers: [{ 17 | kind: 'Raster', 18 | opacity: 1, 19 | colorMap: { 20 | colorMapEntries: [ 21 | { 22 | color: '#00FFFF', 23 | quantity: 100 24 | } 25 | ], 26 | extended: false, 27 | type: 'ramp' 28 | }, 29 | channelSelection: { 30 | grayChannel: { 31 | sourceChannelName: '1' 32 | } 33 | }, 34 | brightnessMax: 1, 35 | brightnessMin: 0, 36 | saturation: 1, 37 | contrast: 1, 38 | resampling: 'linear', 39 | fadeDuration: 200 40 | }] 41 | } 42 | ] 43 | }; 44 | 45 | export default sampleRasterStyle; 46 | -------------------------------------------------------------------------------- /examples/sample.ts: -------------------------------------------------------------------------------- 1 | import { Fstep, Style } from '../index'; 2 | 3 | const sampleStyle: Style = { 4 | name: 'Sample Point Style', 5 | rules: [ 6 | { 7 | name: 'Young Peter', 8 | filter: ['&&', 9 | ['==', 'name', 'Peter'], 10 | ['<=', 'age', 12], 11 | ['<=x<=', 'height', 1, 2] 12 | ], 13 | scaleDenominator: { 14 | min: 500, 15 | max: 1000 16 | }, 17 | symbolizers: [{ 18 | kind: 'Icon', 19 | visibility: false, 20 | allowOverlap: true, 21 | optional: false, 22 | rotationAlignment: 'map', 23 | image: 'http://myserver/getImage', 24 | format: 'image/png' 25 | }, { 26 | kind: 'Line', 27 | blur: 3, 28 | cap: 'square', 29 | join: 'round', 30 | perpendicularOffset: 10, 31 | width: 9, 32 | spacing: 21, 33 | graphicStroke: { 34 | kind: 'Mark', 35 | wellKnownName: 'square' 36 | } 37 | }] 38 | }, 39 | { 40 | name: 'Twelve year old Peter', 41 | filter: ['&&', 42 | ['==', 43 | { 44 | name: 'strMatches', 45 | args: [ 46 | { 47 | name: 'property', 48 | args: ['name'] 49 | }, 50 | 'Peter' 51 | ] 52 | }, 53 | true 54 | ], 55 | ['==', 'age', 12] 56 | ], 57 | scaleDenominator: { 58 | min: 500, 59 | max: 1000 60 | }, 61 | symbolizers: [{ 62 | kind: 'Text', 63 | visibility: false, 64 | allowOverlap: true, 65 | letterSpacing: 12, 66 | optional: false, 67 | rotationAlignment: 'map', 68 | haloColor: '#ff00aa', 69 | haloWidth: 4, 70 | fontStyle: 'italic', 71 | fontWeight: 'bold' 72 | }] 73 | }, 74 | { 75 | name: 'Old Peter', 76 | filter: ['&&', 77 | ['==', 'name', 'Peter'], 78 | ['==', 'age', 50] 79 | ], 80 | scaleDenominator: { 81 | min: 500, 82 | max: 1000 83 | }, 84 | symbolizers: [{ 85 | kind: 'Mark', 86 | wellKnownName: 'circle', 87 | visibility: false, 88 | radius: 5 89 | }, { 90 | kind: 'Mark', 91 | wellKnownName: 'ttf://Webdings#0x68', 92 | radius: { 93 | name: 'step', 94 | args: [ 95 | { 96 | name: 'property', 97 | args: ['population'] 98 | }, 5, 99 | { 100 | boundary: 1000, 101 | value: 10 102 | }, 103 | { 104 | boundary: 10000, 105 | value: 15 106 | }, 107 | { 108 | boundary: 100000, 109 | value: 20 110 | } 111 | ] 112 | }, 113 | color: '#8a000e', 114 | strokeOpacity: 0.7, 115 | strokeColor: '#ffffff' 116 | }] 117 | }, 118 | { 119 | name: 'Very old Peter', 120 | filter: ['&&', 121 | ['==', 'name', 'Peter'], 122 | ['>=', 'age', 51] 123 | ], 124 | scaleDenominator: { 125 | min: 500, 126 | max: 1000 127 | }, 128 | symbolizers: [{ 129 | kind: 'Fill', 130 | color: '#FF0000', 131 | graphicFill: { 132 | kind: 'Icon', 133 | image: '/myimage.png' 134 | } 135 | }, { 136 | kind: 'Raster', 137 | opacity: 1, 138 | colorMap: { 139 | colorMapEntries: [ 140 | { 141 | color: '#00FFFF', 142 | quantity: 100 143 | } 144 | ], 145 | extended: false, 146 | type: 'ramp' 147 | }, 148 | channelSelection: { 149 | grayChannel: { 150 | sourceChannelName: '1' 151 | } 152 | } 153 | }] 154 | } 155 | ] 156 | }; 157 | 158 | export default sampleStyle; 159 | -------------------------------------------------------------------------------- /examples/typeguardsample.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { 3 | isCombinationOperator, 4 | isComparisonOperator, 5 | isFilter, 6 | isNegationOperator, 7 | isOperator 8 | } from '../typeguards'; 9 | 10 | /* 11 | * Hover the variable used in the `console.log`s to check if the typeguard 12 | * is correct and typescript recognizes the correct type. 13 | */ 14 | const comparisonOperator: any = '<'; 15 | const negationOperator: any = '!'; 16 | const combinationOperator: any = '&&'; 17 | const strMatchesFunctionOperator: any = 'FN_strMatches'; 18 | const filter: any[] = [comparisonOperator, 'a', 12]; 19 | if (isFilter(filter)) { 20 | console.log(`${filter} is a Filter`); 21 | } else { 22 | console.log(`${filter} is not a Filter`); 23 | } 24 | 25 | if (isOperator(comparisonOperator)) { 26 | console.log(`${comparisonOperator} is an Operator`); 27 | } 28 | if (isOperator(combinationOperator)) { 29 | console.log(`${combinationOperator} is an Operator`); 30 | } 31 | if (isOperator(negationOperator)) { 32 | console.log(`${negationOperator} is an Operator`); 33 | } 34 | if (isOperator(strMatchesFunctionOperator)) { 35 | console.log(`${strMatchesFunctionOperator} is an Operator`); 36 | } 37 | 38 | if (isComparisonOperator(comparisonOperator)) { 39 | console.log(`${comparisonOperator} is a ComparisonOperator`); 40 | } 41 | if (isCombinationOperator(combinationOperator)) { 42 | console.log(`${combinationOperator} is a CombinationOperator`); 43 | } 44 | if (isNegationOperator(negationOperator)) { 45 | console.log(`${negationOperator} is a NegationOperator`); 46 | } 47 | -------------------------------------------------------------------------------- /functions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { 3 | FunctionCall, 4 | Expression, 5 | PropertyType 6 | } from './style'; 7 | 8 | export type GeoStylerFunction = GeoStylerNumberFunction | 9 | GeoStylerStringFunction | 10 | GeoStylerBooleanFunction | 11 | GeoStylerUnknownFunction; 12 | 13 | /** 14 | * An expression of a function that returns a number. 15 | */ 16 | export type GeoStylerNumberFunction = GeoStylerUnknownFunction | 17 | Fabs | 18 | Facos | 19 | Fadd | 20 | Fasin | 21 | Fatan | 22 | Fatan2 | 23 | Fceil | 24 | Fcos | 25 | Fdiv | 26 | Fexp | 27 | Ffloor | 28 | Finterpolate | 29 | Flog | 30 | Fmax | 31 | Fmin | 32 | Fmodulo | 33 | Fmul | 34 | Fpi | 35 | Fpow | 36 | Frandom | 37 | Frint | 38 | Fround | 39 | Fsin | 40 | Fsqrt | 41 | FstrIndexOf | 42 | FstrLastIndexOf | 43 | FstrLength | 44 | Fsub | 45 | Ftan | 46 | FtoDegrees | 47 | FtoNumber | 48 | FtoRadians; 49 | 50 | /** 51 | * An expression of a function that returns a string. 52 | */ 53 | export type GeoStylerStringFunction = GeoStylerUnknownFunction | 54 | FnumberFormat | 55 | FstrAbbreviate | 56 | FstrCapitalize | 57 | FstrConcat | 58 | FstrDefaultIfBlank | 59 | FstrReplace | 60 | FstrStripAccents | 61 | FstrSubstring | 62 | FstrSubstringStart | 63 | FstrToLowerCase | 64 | FstrToUpperCase | 65 | FstrTrim | 66 | FtoString; 67 | 68 | /** 69 | * An expression of a function that returns a boolean. 70 | */ 71 | export type GeoStylerBooleanFunction = GeoStylerUnknownFunction | 72 | Fall | 73 | Fany | 74 | Fbetween | 75 | Fdouble2bool | 76 | FequalTo | 77 | FgreaterThan | 78 | FgreaterThanOrEqualTo | 79 | Fin | 80 | FlessThan | 81 | FlessThanOrEqualTo | 82 | Fnot | 83 | FnotEqualTo | 84 | FparseBoolean | 85 | FstrEndsWith | 86 | FstrEqualsIgnoreCase | 87 | FstrMatches | 88 | FstrStartsWith; 89 | 90 | export type GeoStylerUnknownFunction = Fcase | Fstep | Fproperty; 91 | 92 | /** 93 | * The absolute value of the specified number value 94 | */ 95 | export interface Fabs extends FunctionCall { 96 | name: 'abs'; 97 | args: [ 98 | Expression 99 | ]; 100 | }; 101 | 102 | /** 103 | * Returns the arc cosine of an angle in radians, in the range of 0.0 through PI 104 | */ 105 | export interface Facos extends FunctionCall { 106 | name: 'acos'; 107 | args: [ 108 | Expression 109 | ]; 110 | }; 111 | 112 | /** 113 | * Returns the sum of the arguments 114 | */ 115 | export interface Fadd extends FunctionCall { 116 | name: 'add'; 117 | args: Expression[]; 118 | }; 119 | 120 | /** 121 | * Resolves to true if all passed arguments resolve to true 122 | */ 123 | export interface Fall extends FunctionCall { 124 | name: 'all'; 125 | args: Expression[]; 126 | }; 127 | 128 | /** 129 | * Resolves to true if any of the passed arguments resolves to true 130 | */ 131 | export interface Fany extends FunctionCall { 132 | name: 'any'; 133 | args: Expression[]; 134 | }; 135 | 136 | /** 137 | * Returns the arc sine of an angle in radians, in the range of -PI / 2 through PI / 2 138 | */ 139 | export interface Fasin extends FunctionCall { 140 | name: 'asin'; 141 | args: [ 142 | Expression 143 | ]; 144 | }; 145 | 146 | /** 147 | * Returns the arc tangent of an angle in radians, in the range of -PI/2 through PI/2 148 | */ 149 | export interface Fatan extends FunctionCall { 150 | name: 'atan'; 151 | args: [ 152 | Expression 153 | ]; 154 | }; 155 | 156 | /** 157 | * Converts a rectangular coordinate (x, y) to polar (r, theta) and returns theta. 158 | */ 159 | export interface Fatan2 extends FunctionCall { 160 | name: 'atan2'; 161 | args: [ 162 | Expression, 163 | Expression 164 | ]; 165 | }; 166 | 167 | /** 168 | * Returns true if arg1 <= arg0 <= arg2 169 | */ 170 | export interface Fbetween extends FunctionCall { 171 | name: 'between'; 172 | args: [ 173 | Expression, 174 | Expression, 175 | Expression 176 | ]; 177 | }; 178 | 179 | export type FCaseParameter = { 180 | case: Expression; 181 | value: Expression; 182 | }; 183 | 184 | /** 185 | * Textual representation of a switch-case function. 186 | * argument[0] is the default value. 187 | * argument[1] - argument[args.length] are objects with 'case' and 'value'. 188 | * 189 | * The value of the first object where its 'case' Expression resolves to true 190 | * will be used. 191 | * If no 'case' expression resolves to true the default value will be returned. 192 | */ 193 | export interface Fcase extends FunctionCall { 194 | name: 'case'; 195 | args: [ 196 | Expression, 197 | ...FCaseParameter[] 198 | ]; 199 | }; 200 | 201 | /** 202 | * Returns the smallest (closest to negative infinity) number value that is greater than or equal to 203 | * x and is equal to a mathematical integer. 204 | */ 205 | export interface Fceil extends FunctionCall { 206 | name: 'ceil'; 207 | args: [ 208 | Expression 209 | ]; 210 | }; 211 | 212 | /** 213 | * Returns the cosine of an angle expressed in radians 214 | */ 215 | export interface Fcos extends FunctionCall { 216 | name: 'cos'; 217 | args: [ 218 | Expression 219 | ]; 220 | }; 221 | 222 | /** 223 | * Returns the division of argument[0] by argument[1] 224 | */ 225 | export interface Fdiv extends FunctionCall { 226 | name: 'div'; 227 | args: [ 228 | Expression, 229 | Expression 230 | ]; 231 | }; 232 | 233 | /** 234 | * Returns true if x is zero, false otherwise 235 | */ 236 | export interface Fdouble2bool extends FunctionCall { 237 | name: 'double2bool'; 238 | args: [ 239 | Expression 240 | ]; 241 | }; 242 | 243 | /** 244 | * Resolves to true if both arguments are equal 245 | */ 246 | export interface FequalTo extends FunctionCall { 247 | name: 'equalTo'; 248 | args: [ 249 | Expression, 250 | Expression 251 | ]; 252 | }; 253 | 254 | /** 255 | * Returns Euler’s number e raised to the power of x 256 | */ 257 | export interface Fexp extends FunctionCall { 258 | name: 'exp'; 259 | args: [ 260 | Expression 261 | ]; 262 | }; 263 | 264 | /** 265 | * Returns the largest (closest to positive infinity) value that is less than or equal to x and is 266 | * equal to a mathematical integer 267 | */ 268 | export interface Ffloor extends FunctionCall { 269 | name: 'floor'; 270 | args: [ 271 | Expression 272 | ]; 273 | }; 274 | 275 | /** 276 | * Resolves to true if argument[0] is greater than argument[1] 277 | */ 278 | export interface FgreaterThan extends FunctionCall { 279 | name: 'greaterThan'; 280 | args: [ 281 | Expression, 282 | Expression 283 | ]; 284 | }; 285 | 286 | /** 287 | * Resolves to true if argument[0] is greater than or equal to argument[1] 288 | */ 289 | export interface FgreaterThanOrEqualTo extends FunctionCall { 290 | name: 'greaterThanOrEqualTo'; 291 | args: [ 292 | Expression, 293 | Expression 294 | ]; 295 | }; 296 | 297 | /** 298 | * Returns true if arguments[0] is equal to one of the arguments[1], …, arguments[n] values. Use the 299 | * function name matching the number of arguments specified. 300 | */ 301 | export interface Fin extends FunctionCall { 302 | name: 'in'; 303 | args: Expression[]; 304 | }; 305 | 306 | /** 307 | * Resolves to true if argument[0] is less than argument[1] 308 | */ 309 | export interface FlessThan extends FunctionCall { 310 | name: 'lessThan'; 311 | args: [ 312 | Expression, 313 | Expression 314 | ]; 315 | }; 316 | 317 | /** 318 | * Resolves to true if argument[0] is less than or equal to argument[1] 319 | */ 320 | export interface FlessThanOrEqualTo extends FunctionCall { 321 | name: 'lessThanOrEqualTo'; 322 | args: [ 323 | Expression, 324 | Expression 325 | ]; 326 | }; 327 | 328 | /** 329 | * Inverts the boolean value of argument[0] 330 | */ 331 | export interface Fnot extends FunctionCall { 332 | name: 'not'; 333 | args: [ 334 | Expression 335 | ]; 336 | }; 337 | 338 | /** 339 | * Resolves to false if both arguments are equal 340 | */ 341 | export interface FnotEqualTo extends FunctionCall { 342 | name: 'notEqualTo'; 343 | args: [ 344 | Expression, 345 | Expression 346 | ]; 347 | }; 348 | 349 | /** 350 | * Returns the natural logarithm (base e) of x 351 | */ 352 | export interface Flog extends FunctionCall { 353 | name: 'log'; 354 | args: [ 355 | Expression 356 | ]; 357 | }; 358 | 359 | /** 360 | * Returns the maximum between argument[0], …, argument[n] 361 | */ 362 | export interface Fmax extends FunctionCall { 363 | name: 'max'; 364 | args: Expression[]; 365 | }; 366 | 367 | /** 368 | * Returns the minimum between argument[0], …, argument[n] 369 | */ 370 | export interface Fmin extends FunctionCall { 371 | name: 'min'; 372 | args: Expression[]; 373 | }; 374 | 375 | /** 376 | * Returns the remainder after integer division of argument[0] by argument[1] 377 | */ 378 | export interface Fmodulo extends FunctionCall { 379 | name: 'modulo'; 380 | args: [ 381 | Expression, 382 | Expression 383 | ]; 384 | }; 385 | 386 | /** 387 | * Returns the product of the arguments 388 | */ 389 | export interface Fmul extends FunctionCall { 390 | name: 'mul'; 391 | args: Expression[]; 392 | }; 393 | 394 | /** 395 | * Formats the number (argument[1]) according to the specified format (arguments[0]) using the default locale 396 | * or the one provided (argument[2]) as an optional argument. The format syntax can be found 397 | * in the Java DecimalFormat javadocs 398 | */ 399 | export interface FnumberFormat extends FunctionCall { 400 | name: 'numberFormat'; 401 | args: [ 402 | Expression, 403 | Expression, 404 | Expression 405 | ]; 406 | }; 407 | 408 | /** 409 | * Parses a string into a boolean. The empty string, f, 0.0 and 0 are considered false, everything 410 | * else is considered true. 411 | */ 412 | export interface FparseBoolean extends FunctionCall { 413 | name: 'parseBoolean'; 414 | args: [ 415 | Expression 416 | ]; 417 | }; 418 | 419 | /** 420 | * Returns an approximation of pi, the ratio of the circumference of a circle to its diameter 421 | */ 422 | export interface Fpi extends Omit, 'args'> { 423 | name: 'pi'; 424 | }; 425 | 426 | /** 427 | * Returns the value of base (argument[0]) raised to the power of exponent (arguments[1]) 428 | */ 429 | export interface Fpow extends FunctionCall { 430 | name: 'pow'; 431 | args: [ 432 | Expression, 433 | Expression 434 | ]; 435 | }; 436 | 437 | /** 438 | * Returns the value of the property argument[0]. Allows property names to be compute 439 | * or specified by Variable substitution in SLD. 440 | */ 441 | export interface Fproperty extends FunctionCall { 442 | name: 'property'; 443 | args: [ 444 | Expression 445 | ]; 446 | }; 447 | 448 | /** 449 | * Returns a Double value with a positive sign, greater than or equal to 0.0 and less than 1.0. 450 | */ 451 | export interface Frandom extends Omit, 'args'> { 452 | name: 'random'; 453 | }; 454 | 455 | /** 456 | * Returns the Double value that is closest in value to the argument and is equal to a mathematical 457 | * integer. If two double values that are mathematical integers are equally close, the result is the 458 | * integer value that is even. 459 | */ 460 | export interface Frint extends FunctionCall { 461 | name: 'rint'; 462 | args: [ 463 | Expression 464 | ]; 465 | }; 466 | 467 | /** 468 | * Returns the closest number to argument[0]. 469 | */ 470 | export interface Fround extends FunctionCall { 471 | name: 'round'; 472 | args: [ 473 | Expression 474 | ]; 475 | }; 476 | 477 | /** 478 | * Returns the sine of an angle expressed in radians 479 | */ 480 | export interface Fsin extends FunctionCall { 481 | name: 'sin'; 482 | args: [ 483 | Expression 484 | ]; 485 | }; 486 | 487 | /** 488 | * Returns the square root of argument[0] 489 | */ 490 | export interface Fsqrt extends FunctionCall { 491 | name: 'sqrt'; 492 | args: [ 493 | Expression 494 | ]; 495 | }; 496 | 497 | export type FStepParameter = { 498 | boundary: Expression; 499 | value: Expression; 500 | }; 501 | 502 | /** 503 | * Returns an unknown value depending on the passed in value of argument[0]. In 504 | * most cases this will be an {@link Fproperty}. 505 | * The argument[1] is the initial value. 506 | * All following arguments are an array of length 2 where the first element is 507 | * a numeric value and the second element is the value to return if the value is 508 | * larger than that boundry. 509 | */ 510 | export interface Fstep extends FunctionCall { 511 | name: 'step'; 512 | args: [ 513 | Expression, Expression, 514 | ...FStepParameter[] 515 | ]; 516 | }; 517 | 518 | /** 519 | * Abbreviates the sentence (argument[0]) at first space beyond lower (argument[1]) 520 | * or at upper (argument[2]) if no space. Appends append (argument[3]) if string is abbreviated. 521 | */ 522 | export interface FstrAbbreviate extends FunctionCall { 523 | name: 'strAbbreviate'; 524 | args: [ 525 | Expression, 526 | Expression, 527 | Expression, 528 | Expression 529 | ]; 530 | }; 531 | 532 | /** 533 | * Fully capitalizes the sentence. For example, “HoW aRe YOU?” will be turned into “How Are You?” 534 | */ 535 | export interface FstrCapitalize extends FunctionCall { 536 | name: 'strCapitalize'; 537 | args: [ 538 | Expression 539 | ]; 540 | }; 541 | 542 | /** 543 | * Concatenates the two strings into one 544 | */ 545 | export interface FstrConcat extends FunctionCall { 546 | name: 'strConcat'; 547 | args: Expression[]; 548 | }; 549 | 550 | /** 551 | * Returns default (argument[1]) if str (argument[0]) is empty, blank or null 552 | */ 553 | export interface FstrDefaultIfBlank extends FunctionCall { 554 | name: 'strDefaultIfBlank'; 555 | args: [ 556 | Expression, 557 | Expression 558 | ]; 559 | }; 560 | 561 | /** 562 | * Returns true if string (argument[0]) ends with suffix (argument[1]) 563 | */ 564 | export interface FstrEndsWith extends FunctionCall { 565 | name: 'strEndsWith'; 566 | args: [ 567 | Expression, 568 | Expression 569 | ]; 570 | }; 571 | 572 | /** 573 | * Returns true if the two strings are equal ignoring case considerations 574 | */ 575 | export interface FstrEqualsIgnoreCase extends FunctionCall { 576 | name: 'strEqualsIgnoreCase'; 577 | args: [ 578 | Expression, 579 | Expression 580 | ]; 581 | }; 582 | 583 | /** 584 | * Returns the index within this string (argument[0]) of the first occurrence of the specified 585 | * substring (argument[1]), or -1 if not found 586 | */ 587 | export interface FstrIndexOf extends FunctionCall { 588 | name: 'strIndexOf'; 589 | args: [ 590 | Expression, 591 | Expression 592 | ]; 593 | }; 594 | 595 | /** 596 | * Returns the index within this string (arguments[0]) of the last occurrence of the specified 597 | * substring (arguments[1]), or -1 if not found 598 | */ 599 | export interface FstrLastIndexOf extends FunctionCall { 600 | name: 'strLastIndexOf'; 601 | args: [ 602 | Expression, 603 | Expression 604 | ]; 605 | }; 606 | 607 | /** 608 | * Returns the string length 609 | */ 610 | export interface FstrLength extends FunctionCall{ 611 | name: 'strLength'; 612 | args: [ 613 | Expression 614 | ]; 615 | }; 616 | 617 | /** 618 | * Returns true if the string (arguments[0]) matches the specified regular expression (arguments[1]). 619 | * For the full syntax of the pattern specification see the Java Pattern class javadocs 620 | */ 621 | export interface FstrMatches extends FunctionCall { 622 | name: 'strMatches'; 623 | args: [ 624 | Expression, 625 | Expression 626 | ]; 627 | }; 628 | 629 | /** 630 | * Returns the string (argument[0]) with the pattern (argument[1]) replaced with the given 631 | * replacement (argument[2]) text. If the global argument (argument[3]) is true then all occurrences of the pattern 632 | * will be replaced, otherwise only the first. For the full syntax of the pattern specification see 633 | * the Java Pattern class javadocs 634 | */ 635 | export interface FstrReplace extends FunctionCall { 636 | name: 'strReplace'; 637 | args: [ 638 | Expression, 639 | Expression, 640 | Expression, 641 | Expression 642 | ]; 643 | }; 644 | 645 | /** 646 | * Returns true if string (argument[0]) starts with prefix (argument[1]). 647 | */ 648 | export interface FstrStartsWith extends FunctionCall { 649 | name: 'strStartsWith'; 650 | args: [ 651 | Expression, 652 | Expression 653 | ]; 654 | }; 655 | 656 | /** 657 | * Removes diacritics (~= accents) from a string. The case will not be altered. 658 | */ 659 | export interface FstrStripAccents extends FunctionCall { 660 | name: 'strStripAccents'; 661 | args: [ 662 | Expression 663 | ]; 664 | }; 665 | 666 | /** 667 | * Returns a new string that is a substring of this string (argument[0]). The substring begins 668 | * at the specified begin (argument[1]) and extends to the character at index endIndex (argument[2]) - 1 669 | * (indexes are zero-based). 670 | */ 671 | export interface FstrSubstring extends FunctionCall { 672 | name: 'strSubstring'; 673 | args: [ 674 | Expression, 675 | Expression, 676 | Expression 677 | ]; 678 | }; 679 | 680 | /** 681 | * Returns a new string that is a substring of this string (argument[0]). The substring begins 682 | * at the specified begin (arguments[1]) and extends to the last character of the string 683 | */ 684 | export interface FstrSubstringStart extends FunctionCall { 685 | name: 'strSubstringStart'; 686 | args: [ 687 | Expression, 688 | Expression 689 | ]; 690 | }; 691 | 692 | /** 693 | * Returns the lower case version of the string 694 | */ 695 | export interface FstrToLowerCase extends FunctionCall { 696 | name: 'strToLowerCase'; 697 | args: [ 698 | Expression 699 | ]; 700 | }; 701 | 702 | /** 703 | * Returns the upper case version of the string 704 | */ 705 | export interface FstrToUpperCase extends FunctionCall { 706 | name: 'strToUpperCase'; 707 | args: [ 708 | Expression 709 | ]; 710 | }; 711 | 712 | /** 713 | * Returns a copy of the string, with leading and trailing blank-space omitted 714 | */ 715 | export interface FstrTrim extends FunctionCall { 716 | name: 'strTrim'; 717 | args: [ 718 | Expression 719 | ]; 720 | }; 721 | 722 | /** 723 | * Returns the result of substracting argument[1] from argument[0] 724 | */ 725 | export interface Fsub extends FunctionCall { 726 | name: 'sub'; 727 | args: [ 728 | Expression, 729 | Expression 730 | ]; 731 | }; 732 | 733 | /** 734 | * Returns the trigonometric tangent of angle expressed in radians 735 | */ 736 | export interface Ftan extends FunctionCall { 737 | name: 'tan'; 738 | args: [ 739 | Expression 740 | ]; 741 | }; 742 | 743 | /** 744 | * Converts an angle expressed in radians into degrees 745 | */ 746 | export interface FtoDegrees extends FunctionCall { 747 | name: 'toDegrees'; 748 | args: [ 749 | Expression 750 | ]; 751 | }; 752 | 753 | /** 754 | * Converts an unknown value into a number 755 | */ 756 | export interface FtoNumber extends FunctionCall { 757 | name: 'toNumber'; 758 | args: [ 759 | Expression 760 | ]; 761 | }; 762 | 763 | /** 764 | * Converts an angle expressed in radians into degrees 765 | */ 766 | export interface FtoRadians extends FunctionCall { 767 | name: 'toRadians'; 768 | args: [ 769 | Expression 770 | ]; 771 | }; 772 | 773 | /** 774 | * Converts an unknown value into a string 775 | */ 776 | export interface FtoString extends FunctionCall { 777 | name: 'strToString'; 778 | args: [ 779 | Expression 780 | ]; 781 | }; 782 | 783 | export type FInterpolateTypeLinear = { 784 | name: 'linear'; 785 | }; 786 | 787 | export type FInterpolateType = FInterpolateTypeLinear; 788 | 789 | export type FInterpolateParameter = { 790 | stop: Expression; 791 | value: Expression; 792 | }; 793 | 794 | /** 795 | * Textual representation of a interpolate function. 796 | * argument[0] is the interpolation type. 797 | * argument[1] is the input value. 798 | * argument[2] - argument[args.length] are objects with 'stop' and 'value'. 799 | * 800 | * Will produce continuous results by interpolating between pairs 801 | * of FInterpolateParamters. 802 | */ 803 | export interface Finterpolate extends FunctionCall { 804 | name: 'interpolate'; 805 | args: [ 806 | FInterpolateType, 807 | Expression, 808 | FInterpolateParameter, 809 | FInterpolateParameter, 810 | ...FInterpolateParameter[] 811 | ]; 812 | }; 813 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './style'; 2 | export * from './functions'; 3 | export * from './typeguards'; 4 | -------------------------------------------------------------------------------- /jest.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: [ 3 | '/test/typeguards.ts' 4 | ], 5 | testMatch: [ 6 | '/test/typeguards.spec.ts' 7 | ], 8 | transform: { 9 | '^.+\\.ts': '/node_modules/babel-jest' 10 | }, 11 | moduleFileExtensions: [ 12 | 'ts', 13 | 'js' 14 | ] 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geostyler-style", 3 | "version": "10.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "type": "module", 7 | "files": [ 8 | "dist/*", 9 | "examples/**", 10 | "schema.json", 11 | "README.md" 12 | ], 13 | "scripts": { 14 | "lint": "eslint -c .eslintrc.cjs --ext .ts . && tsc --noEmit", 15 | "build": "tsc", 16 | "test": "jest", 17 | "generate-schema": "typescript-json-schema tsconfig.json Style --id http://geostyler/geostyler-style.json > schema.json", 18 | "generate-docs": "typedoc index.ts", 19 | "prepublishOnly": "npm run build && npm run generate-schema" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.24.6", 23 | "@babel/plugin-proposal-object-rest-spread": "^7.20.7", 24 | "@babel/preset-env": "^7.24.6", 25 | "@babel/preset-typescript": "^7.24.6", 26 | "@commitlint/cli": "^19.3.0", 27 | "@commitlint/config-conventional": "^19.2.2", 28 | "@semantic-release/changelog": "^6.0.3", 29 | "@semantic-release/git": "^10.0.1", 30 | "@terrestris/eslint-config-typescript": "^5.0.0", 31 | "@types/jest": "^29.5.12", 32 | "@typescript-eslint/parser": "^7.12.0", 33 | "babel-jest": "^29.7.0", 34 | "conventional-changelog-conventionalcommits": "^7.0.2", 35 | "core-js": "^3.37.1", 36 | "eslint": "^8.57.0", 37 | "jest": "^29.7.0", 38 | "semantic-release": "^24.0.0", 39 | "typedoc": "^0.25.13", 40 | "typescript": "^5.4.5", 41 | "typescript-json-schema": "^0.64.0" 42 | }, 43 | "engines": { 44 | "node": ">=20.6.0", 45 | "npm": ">=10.0.0" 46 | }, 47 | "repository": { 48 | "type": "git", 49 | "url": "git+https://github.com/geostyler/geostyler-style.git" 50 | }, 51 | "author": "", 52 | "license": "BSD-2-Clause", 53 | "bugs": { 54 | "url": "https://github.com/geostyler/geostyler/issues" 55 | }, 56 | "homepage": "https://github.com/geostyler/geostyler-style#readme", 57 | "funding": "https://opencollective.com/geostyler" 58 | } 59 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /style.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GeoStylerBooleanFunction, 3 | GeoStylerFunction, 4 | GeoStylerNumberFunction, 5 | GeoStylerStringFunction 6 | } from './functions'; 7 | 8 | /** 9 | * The special character to use to indicate a line break. 10 | */ 11 | export type newlineToken = '/n'; 12 | 13 | /** 14 | * The ScaleDenominator defines a range of scales. 15 | */ 16 | export interface ScaleDenominator { 17 | /** 18 | * Minimum value of the ScaleDenominator. The value is inclusive. 19 | * 20 | */ 21 | min?: Expression; 22 | /** 23 | * Maximum value of the ScaleDenominator. The value is exclusive. 24 | */ 25 | max?: Expression; 26 | } 27 | 28 | /** 29 | * Expression that evaluates to the result of a function 30 | * call on a list of argument expressions. 31 | */ 32 | export interface FunctionCall { 33 | name: T extends string ? GeoStylerStringFunction['name'] : 34 | T extends number ? GeoStylerNumberFunction['name'] : 35 | GeoStylerBooleanFunction['name']; 36 | args: Expression[]; 37 | }; 38 | 39 | /** 40 | * Expressions can be a literal value, a property name or a function call. 41 | */ 42 | export type Expression = 43 | T extends string ? GeoStylerStringFunction | T : 44 | T extends number ? GeoStylerNumberFunction | T : 45 | T extends boolean ? GeoStylerBooleanFunction | T : 46 | T; 47 | 48 | /** 49 | * The type of the Style. 50 | */ 51 | export type StyleType = 'Point' | 'Fill' | 'Line' | 'Raster'; 52 | 53 | /** 54 | * A datatype of a property of the data. 55 | */ 56 | export type PropertyType = string | number | boolean | unknown; 57 | 58 | /** 59 | * The possible Operators used for comparison Filters. 60 | */ 61 | export type ComparisonOperator = '==' | '*=' | '!=' | '<' | '<=' | '>' | '>=' | '<=x<='; 62 | 63 | /** 64 | * The possible Operators used for combination Filters. 65 | */ 66 | export type CombinationOperator = '&&' | '||'; 67 | 68 | /** 69 | * The Operator used for negation Filters. 70 | */ 71 | export type NegationOperator = '!'; 72 | 73 | /** 74 | * All operators. 75 | */ 76 | export type Operator = ComparisonOperator | CombinationOperator | NegationOperator; 77 | 78 | /** 79 | * A Filter that checks if a property is in a range of two values (inclusive). 80 | */ 81 | export type RangeFilter = [ 82 | '<=x<=', 83 | Expression, 84 | Expression, 85 | Expression 86 | ]; 87 | 88 | /** 89 | * A ComparisonFilter compares two values. 90 | * If the first argument is a GeoStylerFunction it will be evaluated it. 91 | * If it is a string it will be treated as key of an object. 92 | */ 93 | export type ComparisonFilter = [ 94 | ComparisonOperator, 95 | Expression, 96 | Expression 97 | ] | RangeFilter; 98 | 99 | /** 100 | * A CombinationFilter combines N Filters with a logical OR / AND operator. 101 | */ 102 | export type CombinationFilter = [ 103 | CombinationOperator, 104 | ...Filter[] 105 | ]; 106 | 107 | /** 108 | * A NegationFilter negates a given Filter. 109 | */ 110 | export type NegationFilter = [ 111 | NegationOperator, 112 | Filter 113 | ]; 114 | 115 | export type Filter = ComparisonFilter | NegationFilter | CombinationFilter | Expression; 116 | 117 | /** 118 | * The kind of the Symbolizer 119 | */ 120 | export type SymbolizerKind = 'Fill' | 'Icon' | 'Line' | 'Text' | 'Mark' | 'Raster'; 121 | 122 | /** 123 | * A Symbolizer describes the style representation of geographical data. 124 | */ 125 | export interface BaseSymbolizer { 126 | /** 127 | * Describes the type of the kind of the Symbolizer. 128 | */ 129 | kind: SymbolizerKind; 130 | /** 131 | * A color defined as a hex-color string. 132 | */ 133 | color?: Expression; 134 | /** 135 | * Determines the total opacity for the Symbolizer. 136 | * A value between 0 and 1. 0 is none opaque and 1 is full opaque. 137 | */ 138 | opacity?: Expression; 139 | /** 140 | * Defines whether the Symbolizer should be visibile or not. 141 | */ 142 | visibility?: Expression; 143 | } 144 | 145 | /** 146 | * A PointSymbolizer describes the style representation of POINT data. 147 | */ 148 | export interface BasePointSymbolizer extends BaseSymbolizer { 149 | /** 150 | * This is a property relevant if using tiled datasets. 151 | * If true, the symbols will not cross tile edges to avoid mutual collisions. 152 | */ 153 | avoidEdges?: Expression; 154 | /** 155 | * The offset of the Symbolizer as [x, y] coordinates. Positive values indicate 156 | * right and down, while negative values indicate left and up. 157 | */ 158 | offset?: [Expression, Expression]; 159 | /** 160 | * Property relevant for mapbox-styles. 161 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-symbol-icon-translate-anchor 162 | */ 163 | offsetAnchor?: Expression<'map' | 'viewport'>; 164 | } 165 | 166 | /** 167 | * Template literal to be more precise on what a font specification of a wellknownname can look like. 168 | * font-based symbols following Geotools/Geoserver syntax: ttf://# 169 | */ 170 | export type FontSpec = `ttf://${string}#0x${string}`; 171 | 172 | /** 173 | * Supported WellKnownNames 174 | * Note that due to TypeScript limitations any string will be valid for this type; this will not change 175 | * until regexp or equivalent is supported, see: 176 | * https://github.com/microsoft/TypeScript/issues/6579 177 | * 178 | */ 179 | export type WellKnownName = 'arrow' | 'arrowhead' | 'asterisk_fill' | 'circle' | 'cross' | 'cross2' 180 | | 'cross_fill' | 'decagon' | 'diagonal_half_square' | 'diamond' | 'equilateral_triangle' 181 | | 'filled_arrowhead' | 'half_arc' | 'half_square' | 'heart' | 'hexagon' | 'horline' | 'left_half_triangle' 182 | | 'line' | 'octagon' | 'parallelogram_left' | 'parallelogram_right' | 'pentagon' | 'quarter_arc' 183 | | 'quarter_circle' | 'quarter_square' | 'right_half_triangle' | 'rounded_square' | 'semi_circle' 184 | | 'shield' | 'square' | 'square_with_corners' | 'star' | 'star_diamond' | 'third_arc' | 'third_circle' 185 | | 'trapezoid' | 'triangle' | 'x' 186 | | 'shape://vertline' | 'shape://horline' | 'shape://slash' 187 | | 'shape://backslash' | 'shape://dot' | 'shape://plus' 188 | | 'shape://times' | 'shape://oarrow' | 'shape://carrow' 189 | | FontSpec; 190 | 191 | /** 192 | * Unit that defines how to handle the corresponding symbolizer property. 193 | * Default should be pixel in your parser. 194 | * 'px' => pixel 195 | * 'm' => meter 196 | */ 197 | export type DistanceUnit = 'px' | 'm'; 198 | 199 | /** 200 | * MarkSymbolizer describes the style representation of POINT data, if styled as 201 | * with a regular geometry. 202 | */ 203 | export interface MarkSymbolizer extends BasePointSymbolizer { 204 | kind: 'Mark'; 205 | /** 206 | * The WellKnownName of the MarkSymbolizer. 207 | */ 208 | wellKnownName: WellKnownName; 209 | /** 210 | * The radius of the Symbolizer. Values describing the full size of the Symbolizer 211 | * have to be divided by two (pixels if radiusUnit is not defined). 212 | */ 213 | radius?: Expression; 214 | /** 215 | * Unit to use for the radius. 216 | */ 217 | radiusUnit?: DistanceUnit; 218 | /** 219 | * The rotation of the Symbolizer in degrees. Value should be between 0 and 360. 220 | */ 221 | rotate?: Expression; 222 | /** 223 | * The opacity of the fill. A value between 0 and 1. 224 | * 0 is none opaque and 1 is full opaque. 225 | */ 226 | fillOpacity?: Expression; 227 | /** 228 | * The color of the stroke represented as a hex-color string. 229 | */ 230 | strokeColor?: Expression; 231 | /** 232 | * The opacity of the stroke. A value between 0 and 1. 233 | * 0 is none opaque and 1 is full opaque. 234 | */ 235 | strokeOpacity?: Expression; 236 | /** 237 | * The width of the stroke (pixels if strokeWidthUnit is not defined). 238 | */ 239 | strokeWidth?: Expression; 240 | /** 241 | * Unit to use for the strokeWidth. 242 | */ 243 | strokeWidthUnit?: DistanceUnit; 244 | /** 245 | * Amount to blur the Symbolizer. 1 blurs the Symbolizer such that only the 246 | * centerpoint has full opacity. Mostly relevant for circles. 247 | */ 248 | blur?: Expression; 249 | /** 250 | * Property relevant for mapbox-styles. 251 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-circle-circle-pitch-alignment 252 | */ 253 | pitchAlignment?: Expression<'map' | 'viewport'>; 254 | /** 255 | * Property relevant for mapbox-styles. 256 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-circle-circle-pitch-scale 257 | */ 258 | pitchScale?: Expression<'map' | 'viewport'>; 259 | } 260 | 261 | /** 262 | * The TextSymbolizer describes the style representation of point data, if styled 263 | * with a text. 264 | */ 265 | export interface TextSymbolizer extends BasePointSymbolizer { 266 | kind: 'Text'; 267 | /** 268 | * If true, the text will be visible even if it collides with other previously 269 | * drawn symbols. 270 | */ 271 | allowOverlap?: Expression; 272 | /** 273 | * The anchor position of the label referred to the center of the geometry. 274 | */ 275 | anchor?: Expression< 276 | 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' 277 | >; 278 | /** 279 | * Template string where {{PROPERTYNAME}} can be used to be replaced by values 280 | * from the dataset. 281 | * e.g.: "Name {{country_name}}" 282 | */ 283 | label?: Expression; 284 | /** 285 | * An Array of fonts. Comparable to https://www.w3schools.com/cssref/pr_font_font-family.asp 286 | */ 287 | font?: Expression[]; 288 | /** 289 | * The halo's fadeout distance towards the outside. 290 | */ 291 | haloBlur?: Expression; 292 | /** 293 | * The color of the text's halo, which helps it stand out from backgrounds 294 | * represented as a hex-color string. 295 | */ 296 | haloColor?: Expression; 297 | /** 298 | * Distance of halo to the font outline (pixels if haloWidthUnit is not defined). 299 | */ 300 | haloWidth?: Expression; 301 | /** 302 | * Unit to use for the haloWidth. 303 | */ 304 | haloWidthUnit?: DistanceUnit; 305 | /** 306 | * The opacity of the halo. A value between 0 and 1. 307 | * 0 means no opacity (i.e. transparent) and 1 is fully opaque. 308 | */ 309 | haloOpacity?: Expression; 310 | /** 311 | * Text justification option to align the text. 312 | */ 313 | justify?: Expression<'left' | 'center' | 'right'>; 314 | /** 315 | * If true, the text will be kept upright. 316 | */ 317 | keepUpright?: Expression; 318 | /** 319 | * Sets the spacing between text characters (pixels if letterSpacingUnit is not defined). 320 | */ 321 | letterSpacing?: Expression; 322 | /** 323 | * Unit to use for the letterSpacing. 324 | */ 325 | letterSpacingUnit?: DistanceUnit | 'em'; 326 | /** 327 | * Sets the line height (pixels if lineHeightUnit is not defined). 328 | * 'em' -> fontsize 329 | */ 330 | lineHeight?: Expression; 331 | /** 332 | * Unit to use for the lineHeight. 333 | * 'em' -> fontsize 334 | */ 335 | lineHeightUnit?: DistanceUnit | 'em'; 336 | /** 337 | * Maximum angle change between adjacent characters in degrees. 338 | */ 339 | maxAngle?: Expression; 340 | /** 341 | * The maximum line width for text wrapping. 342 | */ 343 | maxWidth?: Expression; 344 | /** 345 | * Property relevant for mapbox-styles. 346 | * If true, icons will display without their corresponding text when the text 347 | * collides with other symbols and the icon does not. 348 | */ 349 | optional?: Expression; 350 | /** 351 | * Size of the additional area around the text bounding box used for detecting 352 | * symbol collisions. 353 | */ 354 | padding?: Expression; 355 | /** 356 | * Property relevant for mapbox-styles. 357 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-text-pitch-alignment 358 | */ 359 | pitchAlignment?: Expression<'map' | 'viewport' | 'auto'>; 360 | /** 361 | * The rotation of the Symbolizer in degrees. Value should be between 0 and 360. 362 | */ 363 | rotate?: Expression; 364 | /** 365 | * Property relevant for mapbox-styles. 366 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-text-rotation-alignment 367 | */ 368 | rotationAlignment?: Expression<'map' | 'viewport' | 'auto'>; 369 | /** 370 | * The fontsize in pixels. 371 | */ 372 | size?: Expression; 373 | /** 374 | * Specifies how to capitalize text, similar to the CSS text-transform property. 375 | */ 376 | transform?: Expression<'none' | 'uppercase' | 'lowercase'>; 377 | /** 378 | * Specifies whether a font should be styled with a normal, italic, or oblique 379 | * face from its font-family. 380 | */ 381 | fontStyle?: Expression<'normal' | 'italic' | 'oblique'>; 382 | /** 383 | * Specifies the weight (or boldness) of the font. The weights available depend 384 | * on the font-family you are using. 385 | */ 386 | fontWeight?: Expression<'normal' | 'bold'>; 387 | /** 388 | * Specifies label placement relative to its geometry. 389 | */ 390 | placement?: Expression<'point' | 'line' | 'line-center'>; 391 | } 392 | 393 | /** 394 | * Configuration for a sprite image. 395 | */ 396 | export type Sprite = { 397 | /** 398 | * A path/URL to the sprite image file. 399 | */ 400 | source: Expression; 401 | /** 402 | * The starting position of the sprite to cut out. Origing [0, 0] is top left in pixels. 403 | */ 404 | position: [Expression, Expression]; 405 | /** 406 | * The size of the sprite [width, height] in pixels. 407 | */ 408 | size: [Expression, Expression]; 409 | }; 410 | 411 | /** 412 | * An IconSymbolizer describes the style representation of POINT data if styled 413 | * with a specific icon. 414 | */ 415 | export interface IconSymbolizer extends BasePointSymbolizer { 416 | kind: 'Icon'; 417 | /** 418 | * If true, the icon will be visible even if it collides with other previously 419 | * drawn symbols. 420 | */ 421 | allowOverlap?: Expression; 422 | /** 423 | * Part of the icon placed closest to the anchor. This may conflict with a set 424 | * offset. 425 | */ 426 | anchor?: Expression< 427 | 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' 428 | >; 429 | /** 430 | * The halo's fadeout distance towards the outside. 431 | */ 432 | haloBlur?: Expression; 433 | /** 434 | * The color of the icons halo, which helps it stand out from backgrounds represented 435 | * as a hex-color string. 436 | */ 437 | haloColor?: Expression; 438 | /** 439 | * Distance of halo to the icons outline (pixels if haloWidthUnit is not defined). 440 | */ 441 | haloWidth?: Expression; 442 | /** 443 | * Unit to use for the haloWidth. 444 | */ 445 | haloWidthUnit?: DistanceUnit; 446 | /** 447 | * The opacity of the halo. A value between 0 and 1. 448 | * 0 means no opacity (i.e. transparent) and 1 is fully opaque. 449 | */ 450 | haloOpacity?: Expression; 451 | /** 452 | * A path/URL to the icon image file or a {@link Sprite} configuration. 453 | */ 454 | image?: Expression | Sprite; 455 | /** 456 | * An optional configuration for the image format as MIME type. 457 | * This might be needed if the image(path) has no filending specified. e.g. http://myserver/getImage 458 | */ 459 | format?: Expression<`image/${'png' | 'jpg' | 'jpeg' | 'gif' | 'svg+xml'}`>; 460 | /** 461 | * If true, the icon will be kept upright. 462 | */ 463 | keepUpright?: Expression; 464 | /** 465 | * Property relevant for mapbox-styles. 466 | * If true, text will display without their corresponding icons when the icon 467 | * collides with other symbols and the text does not. 468 | */ 469 | optional?: Expression; 470 | /** 471 | * Size of the additional area around the icon used for detecting symbol collisions. 472 | */ 473 | padding?: Expression; 474 | /** 475 | * Property relevant for mapbox-styles. 476 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-icon-pitch-alignment 477 | */ 478 | pitchAlignment?: Expression<'map' | 'viewport' | 'auto'>; 479 | /** 480 | * The rotation of the Symbolizer in degrees. Value should be between 0 and 360. 481 | */ 482 | rotate?: Expression; 483 | /** 484 | * Property relevant for mapbox-styles. 485 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-icon-rotation-alignment 486 | */ 487 | rotationAlignment?: Expression<'map' | 'viewport' | 'auto'>; 488 | /** 489 | * The Symbolizer size (pixels if sizeUnit is not defined). 490 | */ 491 | size?: Expression; 492 | /** 493 | * Unit to use for the size. 494 | */ 495 | sizeUnit?: DistanceUnit; 496 | /** 497 | * Property relevant for mapbox-styles. 498 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-icon-text-fit 499 | */ 500 | textFit?: Expression<'none' | 'width' | 'height' | 'both'>; 501 | /** 502 | * Property relevant for mapbox-styles. 503 | * Compare https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-icon-text-fit-padding 504 | */ 505 | textFitPadding?: [Expression, Expression, Expression, Expression]; 506 | } 507 | 508 | /** 509 | * A FillSymbolizer describes the style representation of POLYGON data. 510 | */ 511 | export interface FillSymbolizer extends BaseSymbolizer { 512 | kind: 'Fill'; 513 | /** 514 | * Whether the fill should be antialiased or not . 515 | */ 516 | antialias?: Expression; 517 | /** 518 | * The opacity of the fill. A value between 0 and 1. 519 | * 0 is none opaque and 1 is full opaque. 520 | */ 521 | fillOpacity?: Expression; 522 | /** 523 | * The outline color as a hex-color string. Matches the value of fill-color if 524 | * unspecified. 525 | */ 526 | outlineColor?: Expression; 527 | /** 528 | * The opacity of the outline. A value between 0 and 1. 529 | * 0 is none opaque and 1 is full opaque. 530 | */ 531 | outlineOpacity?: Expression; 532 | /** 533 | * The outline width (pixels if outlineWidthUnit is not defined). 534 | */ 535 | outlineWidth?: Expression; 536 | /** 537 | * The Captype for the outLine. 538 | */ 539 | outlineCap?: Expression; 540 | /** 541 | * The JoinType for the outLine. 542 | */ 543 | outlineJoin?: Expression; 544 | /** 545 | * Unit to use for the outlineWidth. 546 | */ 547 | outlineWidthUnit?: DistanceUnit; 548 | /** 549 | * Encodes a dash pattern as an array of numbers. Odd-indexed numbers (first, 550 | * third, etc) determine the length in pixels to draw the line, and even-indexed 551 | * numbers (second, fourth, etc) determine the length in pixels to blank out 552 | * the line. Default is an unbroken line. 553 | */ 554 | outlineDasharray?: Expression[]; 555 | /** 556 | * Renders the fill of the polygon with a repeated pattern of PointSymbolizer. 557 | */ 558 | graphicFill?: PointSymbolizer; 559 | /** 560 | * Size of the additional area around the repeated graphic fill symbolizer. 561 | */ 562 | graphicFillPadding?: [Expression, Expression, Expression, Expression]; 563 | } 564 | 565 | /** 566 | * The Types that are allowed in a graphic 567 | */ 568 | export type GraphicType = 'Mark' | 'Icon'; 569 | 570 | /** 571 | * Determines how lines are rendered at their ends. 572 | * Possible values are butt (sharp square edge), round (rounded edge), 573 | * and square (slightly elongated square edge). 574 | */ 575 | export type CapType = 'butt' | 'round' | 'square'; 576 | 577 | /** 578 | * Determines how lines are rendered at intersections of line segments. 579 | * Possible values are mitre (sharp corner), round (rounded corner), and bevel 580 | * diagonal corner). 581 | */ 582 | export type JoinType = 'bevel' | 'round' | 'miter'; 583 | 584 | /** 585 | * A LineSymbolizer describes the style representation of LINESTRING data. 586 | */ 587 | export interface LineSymbolizer extends BaseSymbolizer { 588 | kind: 'Line'; 589 | blur?: Expression; 590 | /** 591 | * The Captype for the LineSymbolizer. 592 | */ 593 | cap?: Expression; 594 | /** 595 | * Encodes a dash pattern as an array of numbers. Odd-indexed numbers (first, 596 | * third, etc) determine the length in pixels to draw the line, and even-indexed 597 | * numbers (second, fourth, etc) determine the length in pixels to blank out 598 | * the line. Default is an unbroken line. 599 | */ 600 | dasharray?: Expression[]; 601 | /** 602 | * Number of pixels into the dasharray to offset the drawing of the dash, 603 | * used to shift the location of the lines and gaps in a dash. 604 | */ 605 | dashOffset?: Expression; 606 | /** 607 | * Draws a line casing outside of a line's actual path. Value indicates the 608 | * width of the inner gap (pixels if gapWidthUnit is not defined). 609 | */ 610 | gapWidth?: Expression; 611 | /** 612 | * Unit to use for the gapWidth. 613 | */ 614 | gapWidthUnit?: DistanceUnit; 615 | /** 616 | * Defines a gradient with which to color a line feature. 617 | */ 618 | gradient?: any[]; 619 | /** 620 | * Renders the line with a repeated linear PointSymbolizer. 621 | */ 622 | graphicStroke?: PointSymbolizer; 623 | /** 624 | * Renders the pixels of the line with a repeated pattern. 625 | */ 626 | graphicFill?: PointSymbolizer; 627 | /** 628 | * Size of the additional area around the repeated graphic fill symbolizer. 629 | */ 630 | graphicFillPadding?: [Expression, Expression]; 631 | /** 632 | * The JoinType for the LineSymbolizer. 633 | */ 634 | join?: Expression; 635 | /** 636 | * Used to automatically convert miter joins to bevel joins for sharp angles. 637 | */ 638 | miterLimit?: Expression; 639 | /** 640 | * If present, it makes the renderer draw a line parallel to the original one, 641 | * at the given distance. When applied on lines, positive values generate a 642 | * parallel line on the left hand side, negative values on the right hand side. 643 | */ 644 | perpendicularOffset?: Expression; 645 | /** 646 | * Used to automatically convert round joins to miter joins for shallow angles. 647 | */ 648 | roundLimit?: Expression; 649 | /** 650 | * Distance between two symbol anchors (pixels if spacingUnit is not defined). 651 | */ 652 | spacing?: Expression; 653 | /** 654 | * Unit to use for the spacing. 655 | * 'em' -> fontsize 656 | */ 657 | spacingUnit?: DistanceUnit | 'em'; 658 | /** 659 | * The width of the Line (pixels if widthUnit is not defined). 660 | */ 661 | width?: Expression; 662 | /** 663 | * Unit to use for the width. 664 | */ 665 | widthUnit?: DistanceUnit; 666 | } 667 | 668 | /** 669 | * Operators used for Point symbolization. 670 | */ 671 | export type PointSymbolizer = IconSymbolizer | MarkSymbolizer | TextSymbolizer; 672 | 673 | /** 674 | * A single entry for the ColorMap. 675 | */ 676 | export interface ColorMapEntry { 677 | color: Expression; 678 | quantity?: Expression; 679 | label?: Expression; 680 | opacity?: Expression; 681 | } 682 | 683 | /** 684 | * The Types that are allowed in a ColorMap. 685 | */ 686 | export type ColorMapType = 'ramp' | 'intervals' | 'values'; 687 | 688 | /** 689 | * A ColorMap defines the color values for the pixels of a raster image. 690 | */ 691 | export interface ColorMap { 692 | type: Expression; 693 | colorMapEntries?: ColorMapEntry[]; 694 | extended?: Expression; 695 | } 696 | 697 | /** 698 | * A ContrastEnhancement defines how the contrast of image data should be enhanced. 699 | */ 700 | export interface ContrastEnhancement { 701 | enhancementType?: Expression<'normalize' | 'histogram'>; 702 | gammaValue?: Expression; 703 | } 704 | 705 | /** 706 | * A Channel defines the properties for a color channel. 707 | */ 708 | export interface Channel { 709 | sourceChannelName?: Expression; 710 | contrastEnhancement?: ContrastEnhancement; 711 | } 712 | 713 | /** 714 | * A RGBChannel defines how dataset bands are mapped to image color channels. 715 | */ 716 | export interface RGBChannel { 717 | redChannel: Channel; 718 | blueChannel: Channel; 719 | greenChannel: Channel; 720 | } 721 | 722 | /** 723 | * A GrayChannel defines how a single dataset band is mapped to a grayscale channel. 724 | */ 725 | export interface GrayChannel { 726 | grayChannel: Channel; 727 | } 728 | 729 | export type ChannelSelection = RGBChannel | GrayChannel; 730 | 731 | /** 732 | * A RasterSymbolizer defines the style representation of RASTER data. 733 | */ 734 | export interface RasterSymbolizer { 735 | kind: 'Raster'; 736 | /** 737 | * Defines whether the Symbolizer should be visible or not. 738 | */ 739 | visibility?: Expression; 740 | /** 741 | * Determines the total opacity for the Symbolizer. 742 | * A value between 0 and 1. 0 is none opaque and 1 is fully opaque. 743 | */ 744 | opacity?: Expression; 745 | /** 746 | * Defines the color values for the pixels of a raster image, 747 | * as either color gradients, or a mapping of specific values to fixed colors. 748 | */ 749 | colorMap?: ColorMap; 750 | /** 751 | * Specifies how dataset bands are mapped to image color channels. 752 | */ 753 | channelSelection?: ChannelSelection; 754 | /** 755 | * Can be used to adjust the relative brightness of the image data. 756 | */ 757 | contrastEnhancement?: ContrastEnhancement; 758 | hueRotate?: Expression; 759 | brightnessMin?: Expression; 760 | brightnessMax?: Expression; 761 | saturation?: Expression; 762 | contrast?: Expression; 763 | resampling?: Expression<'linear' | 'nearest'>; 764 | fadeDuration?: Expression; 765 | } 766 | 767 | /** 768 | * All operators. 769 | */ 770 | export type Symbolizer = PointSymbolizer | LineSymbolizer | FillSymbolizer | RasterSymbolizer | TextSymbolizer; 771 | 772 | /** 773 | * A Rule combines a specific amount of data (defined by a Filter and a 774 | * ScaleDenominator) and an associated Symbolizer. 775 | */ 776 | export interface Rule { 777 | name: string; 778 | filter?: Filter; 779 | scaleDenominator?: ScaleDenominator; 780 | symbolizers: Symbolizer[]; 781 | } 782 | 783 | /** 784 | * The Style is the main interface and the root for all other interfaces. 785 | */ 786 | export interface Style { 787 | name: string; 788 | rules: Rule[]; 789 | metadata?: { 790 | [key: string]: any; 791 | }; 792 | } 793 | 794 | /** 795 | * Interface for defining unsupported properties in the parsers. 796 | */ 797 | export interface UnsupportedProperties { 798 | ScaleDenominator?: SupportDef; 799 | Filter?: SupportDef | { 800 | '&&'?: SupportDef; 801 | '!'?: SupportDef; 802 | '||'?: SupportDef; 803 | '>'?: SupportDef; 804 | '>='?: SupportDef; 805 | '=='?: SupportDef; 806 | '<'?: SupportDef; 807 | '<='?: SupportDef; 808 | '!='?: SupportDef; 809 | '*='?: SupportDef; 810 | }; 811 | Symbolizer?: SupportDef | { 812 | LineSymbolizer?: SupportDef | { 813 | [key in keyof Required]?: SupportDef 814 | }; 815 | FillSymbolizer?: SupportDef | { 816 | [key in keyof Required]?: SupportDef 817 | }; 818 | MarkSymbolizer?: SupportDef | { 819 | [key in keyof Required]?: SupportDef 820 | }; 821 | IconSymbolizer?: SupportDef | { 822 | [key in keyof Required]?: SupportDef 823 | }; 824 | TextSymbolizer?: SupportDef | { 825 | [key in keyof Required]?: SupportDef 826 | }; 827 | RasterSymbolizer?: SupportDef | { 828 | [key in keyof Required]?: SupportDef 829 | }; 830 | }; 831 | Function?: SupportDef | { 832 | [key in GeoStylerFunction['name']]?: SupportDef; 833 | }; 834 | } 835 | 836 | /** 837 | * Level of support for a functionality. 838 | */ 839 | export type SupportLevel = 'partial' | 'none'; 840 | 841 | /** 842 | * Detailed information about the support of a functionality. 843 | * Can contain an info text. 844 | */ 845 | export type SupportInfo = { 846 | support: SupportLevel; 847 | info?: string; 848 | }; 849 | 850 | /** 851 | * Defines in which way a functionality is supported. 852 | */ 853 | export type SupportDef = SupportInfo | SupportLevel; 854 | 855 | /** 856 | * The Result of the readStyle function of a StyleParser. 857 | */ 858 | export type ReadStyleResult = { 859 | /** 860 | * A list of warnings occured while reading the stlye. 861 | */ 862 | warnings?: string[]; 863 | /** 864 | * A list of unsupportedProperties used while reading the style. 865 | */ 866 | unsupportedProperties?: UnsupportedProperties; 867 | /** 868 | * The geostyler-style as read by the parser. 869 | */ 870 | output?: Style; 871 | /** 872 | * A list of errors occured while reading the style. 873 | */ 874 | errors?: Error[]; 875 | }; 876 | 877 | /** 878 | * The Result of the writeStyle function of a StyleParser. 879 | */ 880 | export type WriteStyleResult = { 881 | /** 882 | * A list of warnings occured while writing the style. 883 | */ 884 | warnings?: string[]; 885 | /** 886 | * A list of unsupportedProperties used while writing the style. 887 | */ 888 | unsupportedProperties?: UnsupportedProperties; 889 | /** 890 | * The target-style as written by the parser. 891 | */ 892 | output?: T; 893 | /** 894 | * A list of errors occured while writing the style. 895 | */ 896 | errors?: Error[]; 897 | }; 898 | 899 | /** 900 | * Interface, which has to be implemented by all GeoStyler style parser classes. 901 | */ 902 | export interface StyleParser { 903 | /** 904 | * The name of the Parser 905 | */ 906 | title: string; 907 | 908 | /** 909 | * Object specifying which properties are not or just partially supported. 910 | */ 911 | unsupportedProperties?: UnsupportedProperties; 912 | 913 | /** 914 | * Parses the inputStyle and transforms it to the GeoStyler Style 915 | * 916 | * @param inputStyle 917 | */ 918 | readStyle(inputStyle: T): Promise; 919 | 920 | /** 921 | * Reads the GeoStyler Style and transforms it to the target Style 922 | * representation. 923 | * 924 | * @param geoStylerStyle Style 925 | */ 926 | writeStyle(geoStylerStyle: Style): Promise>; 927 | 928 | /** 929 | * Parses an input Rule and transforms it to a GeoStyler Rule 930 | * 931 | * @param inputRule 932 | */ 933 | readRule?(inputRule: any): Promise; 934 | 935 | /** 936 | * Reads a GeoStyler Rule and transforms it to a target Rule 937 | * representation. 938 | * 939 | * @param geoStylerRule Rule 940 | */ 941 | writeRule?(geoStylerRule: Rule): Promise; 942 | 943 | /** 944 | * Parses an input Filter and transforms it to a GeoStyler Filter 945 | * 946 | * @param inputFilter 947 | */ 948 | readFilter?(inputFilter: any): Promise; 949 | 950 | /** 951 | * Reads a GeoStyler Filter and transforms it to a target Filter 952 | * representation. 953 | * 954 | * @param geoStylerFilter Filter 955 | */ 956 | writeFilter?(geoStylerFilter: Filter): Promise; 957 | 958 | /** 959 | * Parses an input ScaleDenominator and transforms it to a GeoStyler 960 | * ScaleDenominator 961 | * 962 | * @param inputScaleDenominator 963 | */ 964 | readScaleDenominator?(inputScaleDenominator: any): Promise; 965 | 966 | /** 967 | * Reads a GeoStyler ScaleDenominator and transforms it to a target 968 | * ScaleDenominator representation 969 | * 970 | * @param geoStylerScaleDenominator ScaleDenominator 971 | */ 972 | writeScaleDenominator?(geoStylerScaleDenominator: ScaleDenominator): Promise; 973 | 974 | /** 975 | * Parses an input Symbolizer and transforms it to a GeoStyler Symbolizer 976 | * 977 | * @param inputSymbolizer 978 | */ 979 | readSymbolizer?(inputSymbolizer: any): Promise; 980 | 981 | /** 982 | * Reads a GeoStyler Symbolizer and transforms it to a target Symbolizer 983 | * representation 984 | * 985 | * @param geoStylerSymbolizer Symbolizer 986 | */ 987 | writeSymbolizer?(geoStylerSymbolizer: Symbolizer): Promise; 988 | } 989 | -------------------------------------------------------------------------------- /test/typeguards.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GeoStylerBooleanFunction, 3 | GeoStylerNumberFunction, 4 | GeoStylerStringFunction, 5 | GeoStylerUnknownFunction 6 | } from '../functions'; 7 | import { 8 | CombinationFilter, 9 | CombinationOperator, 10 | ComparisonFilter, 11 | ComparisonOperator, 12 | FillSymbolizer, 13 | Filter, 14 | GrayChannel, 15 | IconSymbolizer, 16 | LineSymbolizer, 17 | MarkSymbolizer, 18 | NegationFilter, 19 | NegationOperator, 20 | RGBChannel, 21 | RasterSymbolizer, 22 | Rule, 23 | ScaleDenominator, 24 | Sprite, 25 | TextSymbolizer 26 | } from '../style'; 27 | import { 28 | isCombinationFilter, 29 | isCombinationOperator, 30 | isComparisonFilter, 31 | isComparisonOperator, 32 | isFillSymbolizer, 33 | isFilter, 34 | isGeoStylerBooleanFunction, 35 | isGeoStylerFunction, 36 | isGeoStylerNumberFunction, 37 | isGeoStylerStringFunction, 38 | isGeoStylerUnknownFunction, 39 | isGrayChannel, 40 | isIconSymbolizer, 41 | isMarkSymbolizer, 42 | isNegationFilter, 43 | isNegationOperator, 44 | isOperator, 45 | isPointSymbolizer, 46 | isPropertyType, 47 | isRasterSymbolizer, 48 | isRgbChannel, 49 | isRule, 50 | isScaleDenominator, 51 | isSprite, 52 | isSymbolizer, 53 | isTextSymbolizer 54 | } from '../typeguards'; 55 | 56 | 57 | const scaleDenominator1: ScaleDenominator = { 58 | min: 100, 59 | max: 200 60 | }; 61 | const scaleDenominator2: ScaleDenominator = { 62 | min: 100 63 | }; 64 | const scaleDenominator3: ScaleDenominator = { 65 | max: { 66 | name: 'pi' 67 | } 68 | }; 69 | 70 | const comparisonOperators: ComparisonOperator[] = [ 71 | '!=', '*=', '<', '<=', '<=x<=', '==', '>', '>=' 72 | ]; 73 | 74 | const combinationOperators: CombinationOperator[] = [ 75 | '&&', '||' 76 | ]; 77 | 78 | const negationOperator: NegationOperator = '!'; 79 | 80 | const comparisonFilter: ComparisonFilter = ['==', 'name', 'peter']; 81 | const comparisonFilterWithFunction1: ComparisonFilter = ['>', { name: 'random' }, 0.5]; 82 | const comparisonFilterWithFunction2: ComparisonFilter = ['==', 'city', { 83 | name: 'strToUpperCase', 84 | args: ['berlin'] 85 | }]; 86 | 87 | const functionAsFilter: Filter = { 88 | name: 'double2bool', 89 | args: [1] 90 | }; 91 | const negationFilter1: NegationFilter = ['!', comparisonFilter]; 92 | const negationFilter2: NegationFilter = ['!', functionAsFilter]; 93 | 94 | const combinationFilter: CombinationFilter = ['&&', 95 | comparisonFilter, 96 | comparisonFilterWithFunction1, 97 | comparisonFilterWithFunction2, 98 | functionAsFilter 99 | ]; 100 | 101 | const iconSymbolizer: IconSymbolizer = { 102 | kind: 'Icon', 103 | image: 'https://avatars1.githubusercontent.com/u/1849416?s=460&v=4', 104 | opacity: 0.5, 105 | rotate: 45, 106 | size: 12, 107 | offset: [10, 20] 108 | }; 109 | 110 | const markSymbolizer: MarkSymbolizer = { 111 | kind: 'Mark', 112 | wellKnownName: 'ttf://My Font Name#0x7c', 113 | color: '#FF0000', 114 | radius: 12, 115 | rotate: 0, 116 | strokeColor: '#112233', 117 | offset: [10, 20] 118 | }; 119 | 120 | const textSymbolizer: TextSymbolizer = { 121 | kind: 'Text', 122 | color: '#000000', 123 | label: '{{name}}', 124 | font: ['Arial'], 125 | size: 12, 126 | offset: [0, 5], 127 | haloColor: '#000000', 128 | haloWidth: 5, 129 | rotate: 45 130 | }; 131 | 132 | const fillSymbolizer: FillSymbolizer = { 133 | kind: 'Fill', 134 | graphicFill: { 135 | kind: 'Mark', 136 | wellKnownName: 'circle' 137 | } 138 | }; 139 | 140 | const lineSymbolizer: LineSymbolizer = { 141 | kind: 'Line', 142 | color: '#FF0000', 143 | width: 5, 144 | dasharray: undefined, 145 | opacity: undefined 146 | }; 147 | 148 | const rasterSymbolizer: RasterSymbolizer = { 149 | kind:'Raster', 150 | colorMap:{ 151 | type:'intervals', 152 | colorMapEntries:[ 153 | { 154 | color:'#ffffff', 155 | quantity:1.0001, 156 | opacity:1 157 | }, 158 | { 159 | color:'#ff0000', 160 | quantity:50000.0001, 161 | opacity:1 162 | }, 163 | { 164 | color:'#ffff00', 165 | quantity:100000.0001, 166 | opacity:1 167 | }, 168 | { 169 | color:'#00aa00', 170 | quantity:10000000, 171 | label:'100000 < x', 172 | opacity:1 173 | } 174 | ] 175 | } 176 | }; 177 | 178 | const rule: Rule = { 179 | name: 'my rule', 180 | filter: combinationFilter, 181 | symbolizers: [ 182 | fillSymbolizer, 183 | lineSymbolizer, 184 | rasterSymbolizer, 185 | markSymbolizer, 186 | textSymbolizer 187 | ] 188 | }; 189 | 190 | const rgbChannel: RGBChannel = { 191 | redChannel: { 192 | sourceChannelName: '1' 193 | }, 194 | blueChannel: { 195 | sourceChannelName: '2', 196 | contrastEnhancement: { 197 | enhancementType: 'histogram', 198 | gammaValue: 2 199 | } 200 | }, 201 | greenChannel: { 202 | sourceChannelName: '3', 203 | contrastEnhancement: { 204 | enhancementType: 'normalize' 205 | } 206 | } 207 | }; 208 | 209 | const grayChannel: GrayChannel = { 210 | grayChannel: { 211 | sourceChannelName: '3', 212 | contrastEnhancement: { 213 | enhancementType: 'normalize' 214 | } 215 | } 216 | }; 217 | 218 | const numberFunction1: GeoStylerNumberFunction = { 219 | name: 'floor', 220 | args: [23.234] 221 | }; 222 | 223 | const numberFunction2: GeoStylerNumberFunction = { 224 | name: 'pi' 225 | }; 226 | 227 | const stringFunction: GeoStylerStringFunction = { 228 | name:'strToLowerCase', 229 | args: ['Berlin'] 230 | }; 231 | 232 | const booleanFunction: GeoStylerBooleanFunction = { 233 | name:'in', 234 | args: [{ 235 | name: 'property', 236 | args: ['city'], 237 | }, 'Berlin', 'Hamburg', 'Bremen'] 238 | }; 239 | 240 | const unknownFunction: GeoStylerUnknownFunction = { 241 | name: 'property', 242 | args: ['city'] 243 | }; 244 | 245 | const spriteImage: Sprite = { 246 | source: 'http://peter.de/sprite', 247 | position: [{ 248 | name: 'property', 249 | args: ['width'], 250 | }, { 251 | name: 'property', 252 | args: ['height'], 253 | }], 254 | size: [48, 48] 255 | }; 256 | 257 | const peter = 'peter'; 258 | const twelve = 12; 259 | const tru = true; 260 | const nul = null; 261 | 262 | const thingsToTest = [ 263 | scaleDenominator1, 264 | scaleDenominator2, 265 | scaleDenominator3, 266 | ...comparisonOperators, 267 | ...combinationOperators, 268 | negationOperator, 269 | peter, 270 | twelve, 271 | tru, 272 | nul, 273 | combinationFilter, 274 | comparisonFilter, 275 | comparisonFilterWithFunction1, 276 | comparisonFilterWithFunction2, 277 | negationFilter1, 278 | negationFilter2, 279 | markSymbolizer, 280 | iconSymbolizer, 281 | textSymbolizer, 282 | lineSymbolizer, 283 | fillSymbolizer, 284 | rasterSymbolizer, 285 | rule, 286 | rgbChannel, 287 | grayChannel, 288 | numberFunction1, 289 | numberFunction2, 290 | stringFunction, 291 | booleanFunction, 292 | unknownFunction, 293 | spriteImage 294 | ]; 295 | 296 | describe('typeguards', () => { 297 | it('isPropertyType', () => { 298 | const expectedMatches: any[] = [ 299 | ...combinationOperators, 300 | ...comparisonOperators, 301 | negationOperator, 302 | peter, 303 | twelve, 304 | tru, 305 | nul 306 | ]; 307 | thingsToTest.forEach(thing => { 308 | expect(isPropertyType(thing)).toBe(expectedMatches.includes(thing)); 309 | }); 310 | }); 311 | it('isScaleDenominator', () => { 312 | const expectedMatches: any[] = [ 313 | scaleDenominator1, 314 | scaleDenominator2, 315 | scaleDenominator3 316 | ]; 317 | thingsToTest.forEach(thing => { 318 | expect(isScaleDenominator(thing)).toBe(expectedMatches.includes(thing)); 319 | }); 320 | }); 321 | it('isOperator', () => { 322 | const expectedMatches: any[] = [ 323 | ...combinationOperators, 324 | ...comparisonOperators, 325 | negationOperator 326 | ]; 327 | thingsToTest.forEach(thing => { 328 | expect(isOperator(thing)).toBe(expectedMatches.includes(thing)); 329 | }); 330 | }); 331 | 332 | it('isComparisonOperator', () => { 333 | const expectedMatches: any[] = [ 334 | ...comparisonOperators, 335 | ]; 336 | thingsToTest.forEach(thing => { 337 | expect(isComparisonOperator(thing)).toBe(expectedMatches.includes(thing)); 338 | }); 339 | }); 340 | 341 | it('isCombinationOperator', () => { 342 | const expectedMatches: any[] = [ 343 | ...combinationOperators 344 | ]; 345 | thingsToTest.forEach(thing => { 346 | expect(isCombinationOperator(thing)).toBe(expectedMatches.includes(thing)); 347 | }); 348 | }); 349 | it('isNegationOperator', () => { 350 | const expectedMatches: any[] = [ 351 | negationOperator 352 | ]; 353 | thingsToTest.forEach(thing => { 354 | expect(isNegationOperator(thing)).toBe(expectedMatches.includes(thing)); 355 | }); 356 | }); 357 | it('isFilter', () => { 358 | const expectedMatches: any[] = [ 359 | combinationFilter, 360 | comparisonFilter, 361 | comparisonFilterWithFunction1, 362 | comparisonFilterWithFunction2, 363 | negationFilter1, 364 | negationFilter2, 365 | booleanFunction, 366 | tru 367 | ]; 368 | thingsToTest.forEach(thing => { 369 | expect(isFilter(thing)).toBe(expectedMatches.includes(thing)); 370 | }); 371 | }); 372 | it('isComparisonFilter', () => { 373 | const expectedMatches: any[] = [ 374 | comparisonFilter, 375 | comparisonFilterWithFunction1, 376 | comparisonFilterWithFunction2 377 | ]; 378 | thingsToTest.forEach(thing => { 379 | expect(isComparisonFilter(thing)).toBe(expectedMatches.includes(thing)); 380 | }); 381 | }); 382 | it('isCombinationFilter', () => { 383 | const expectedMatches: any[] = [ 384 | combinationFilter 385 | ]; 386 | thingsToTest.forEach(thing => { 387 | expect(isCombinationFilter(thing)).toBe(expectedMatches.includes(thing)); 388 | }); 389 | }); 390 | it('isNegationFilter', () => { 391 | const expectedMatches: any[] = [ 392 | negationFilter1, 393 | negationFilter2 394 | ]; 395 | thingsToTest.forEach(thing => { 396 | expect(isNegationFilter(thing)).toBe(expectedMatches.includes(thing)); 397 | }); 398 | }); 399 | it('isSymbolizer', () => { 400 | const expectedMatches: any[] = [ 401 | markSymbolizer, 402 | iconSymbolizer, 403 | textSymbolizer, 404 | lineSymbolizer, 405 | fillSymbolizer, 406 | rasterSymbolizer 407 | ]; 408 | thingsToTest.forEach(thing => { 409 | expect(isSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 410 | }); 411 | }); 412 | it('isPointSymbolizer', () => { 413 | const expectedMatches: any[] = [ 414 | markSymbolizer, 415 | iconSymbolizer, 416 | textSymbolizer 417 | ]; 418 | thingsToTest.forEach(thing => { 419 | expect(isPointSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 420 | }); 421 | }); 422 | it('isIconSymbolizer', () => { 423 | const expectedMatches: any[] = [ 424 | iconSymbolizer 425 | ]; 426 | thingsToTest.forEach(thing => { 427 | expect(isIconSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 428 | }); 429 | }); 430 | it('isTextSymbolizer', () => { 431 | const expectedMatches: any[] = [ 432 | textSymbolizer 433 | ]; 434 | thingsToTest.forEach(thing => { 435 | expect(isTextSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 436 | }); 437 | }); 438 | it('isMarkSymbolizer', () => { 439 | const expectedMatches: any[] = [ 440 | markSymbolizer 441 | ]; 442 | thingsToTest.forEach(thing => { 443 | expect(isMarkSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 444 | }); 445 | }); 446 | it('isFillSymbolizer', () => { 447 | const expectedMatches: any[] = [ 448 | fillSymbolizer 449 | ]; 450 | thingsToTest.forEach(thing => { 451 | expect(isFillSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 452 | }); 453 | }); 454 | it('isRasterSymbolizer', () => { 455 | const expectedMatches: any[] = [ 456 | rasterSymbolizer 457 | ]; 458 | thingsToTest.forEach(thing => { 459 | expect(isRasterSymbolizer(thing)).toBe(expectedMatches.includes(thing)); 460 | }); 461 | }); 462 | it('isRule', () => { 463 | const expectedMatches: any[] = [ 464 | rule 465 | ]; 466 | thingsToTest.forEach(thing => { 467 | expect(isRule(thing)).toBe(expectedMatches.includes(thing)); 468 | }); 469 | }); 470 | it('isRgbChannel', () => { 471 | const expectedMatches: any[] = [ 472 | rgbChannel 473 | ]; 474 | thingsToTest.forEach(thing => { 475 | expect(isRgbChannel(thing)).toBe(expectedMatches.includes(thing)); 476 | }); 477 | }); 478 | it('isGrayChannel', () => { 479 | const expectedMatches: any[] = [ 480 | grayChannel, 481 | functionAsFilter 482 | ]; 483 | thingsToTest.forEach(thing => { 484 | expect(isGrayChannel(thing)).toBe(expectedMatches.includes(thing)); 485 | }); 486 | }); 487 | it('isGeoStylerNumberFunction', () => { 488 | const expectedMatches: any[] = [ 489 | numberFunction1, 490 | numberFunction2 491 | ]; 492 | thingsToTest.forEach(thing => { 493 | expect(isGeoStylerNumberFunction(thing)).toBe(expectedMatches.includes(thing)); 494 | }); 495 | }); 496 | it('isGeoStylerStringFunction', () => { 497 | const expectedMatches: any[] = [ 498 | stringFunction 499 | ]; 500 | thingsToTest.forEach(thing => { 501 | expect(isGeoStylerStringFunction(thing)).toBe(expectedMatches.includes(thing)); 502 | }); 503 | }); 504 | it('isGeoStylerBooleanFunction', () => { 505 | const expectedMatches: any[] = [ 506 | booleanFunction, 507 | functionAsFilter 508 | ]; 509 | thingsToTest.forEach(thing => { 510 | expect(isGeoStylerBooleanFunction(thing)).toBe(expectedMatches.includes(thing)); 511 | }); 512 | }); 513 | it('isGeoStylerUnknownFunction', () => { 514 | const expectedMatches: any[] = [ 515 | unknownFunction 516 | ]; 517 | thingsToTest.forEach(thing => { 518 | expect(isGeoStylerUnknownFunction(thing)).toBe(expectedMatches.includes(thing)); 519 | }); 520 | }); 521 | it('isGeoStylerFunction', () => { 522 | const expectedMatches: any[] = [ 523 | numberFunction1, 524 | numberFunction2, 525 | stringFunction, 526 | booleanFunction, 527 | unknownFunction, 528 | functionAsFilter 529 | ]; 530 | thingsToTest.forEach(thing => { 531 | expect(isGeoStylerFunction(thing)).toBe(expectedMatches.includes(thing)); 532 | }); 533 | }); 534 | 535 | it('isSprite', () => { 536 | const expectedMatches: any[] = [ 537 | spriteImage 538 | ]; 539 | thingsToTest.forEach(thing => { 540 | expect(isSprite(thing)).toBe(expectedMatches.includes(thing)); 541 | }); 542 | }); 543 | 544 | }); 545 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "baseUrl": ".", 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "inlineSources": true, 9 | "lib": ["ES2022", "es6", "dom"], 10 | "module": "Preserve", 11 | "moduleResolution": "Bundler", 12 | "noImplicitAny": true, 13 | "noImplicitReturns": true, 14 | "noImplicitThis": true, 15 | "noUnusedLocals": true, 16 | "outDir": "dist", 17 | "rootDir": ".", 18 | "sourceMap": true, 19 | "strictNullChecks": true, 20 | "target": "ES2022" 21 | }, 22 | "files": [ 23 | "./typeguards.ts", 24 | "./index.ts", 25 | "./functions.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /typeguards.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains typeguards for geostyler-style 3 | * https://basarat.gitbook.io/typescript/type-system/typeguard#user-defined-type-guards 4 | */ 5 | 6 | function isString(str: any){ 7 | if (str != null && typeof str.valueOf() === 'string') { 8 | return true; 9 | } 10 | return false; 11 | }; 12 | 13 | function isNumber(num: any){ 14 | if (num != null && typeof num.valueOf() === 'number') { 15 | return true; 16 | } 17 | return false; 18 | } 19 | 20 | function isBoolean(bool: any){ 21 | if (bool != null && typeof bool.valueOf() === 'boolean') { 22 | return true; 23 | } 24 | return false; 25 | }; 26 | 27 | import { 28 | CombinationFilter, 29 | CombinationOperator, 30 | ComparisonFilter, 31 | ComparisonOperator, 32 | FillSymbolizer, 33 | Filter, 34 | GrayChannel, 35 | IconSymbolizer, 36 | LineSymbolizer, 37 | MarkSymbolizer, 38 | NegationFilter, 39 | NegationOperator, 40 | Operator, 41 | PropertyType, 42 | RasterSymbolizer, 43 | RGBChannel, 44 | Rule, 45 | ScaleDenominator, 46 | TextSymbolizer, 47 | Expression, 48 | GeoStylerBooleanFunction, 49 | GeoStylerNumberFunction, 50 | GeoStylerStringFunction, 51 | GeoStylerUnknownFunction, 52 | GeoStylerFunction, 53 | PointSymbolizer, 54 | Symbolizer, 55 | FunctionCall, 56 | Sprite 57 | } from './index'; 58 | 59 | export const isExpression = (got: any): got is Expression => { 60 | return isGeoStylerFunction(got) || isPropertyType(got); 61 | }; 62 | 63 | /** 64 | * @deprecated use isFunction instead 65 | */ 66 | export const isFunctionCall = (got: any): got is FunctionCall => { 67 | return got.type === 'functioncall' && 68 | got.hasOwnProperty('name') && 69 | isString(got.name) && 70 | got.hasOwnProperty('args') && 71 | Array.isArray(got.args) && 72 | got.args.every((arg: any) => isExpression(arg)); 73 | }; 74 | 75 | // PropertyValue 76 | export const isPropertyType = (got: any): got is PropertyType => { 77 | return isString(got) || isNumber(got) || isBoolean(got) || got === null; 78 | }; 79 | 80 | // ScaleDenominator 81 | export const isScaleDenominator = (got: any): got is ScaleDenominator => { 82 | return !!((got?.min || got?.max) && 83 | ((!!got.min) ? isGeoStylerNumberFunction(got.min) || isNumber(got.min) : true) && 84 | ((!!got.max) ? isGeoStylerNumberFunction(got.max) || isNumber(got.max) : true)); 85 | }; 86 | 87 | // Operators 88 | export const isOperator = (got: any): got is Operator => { 89 | return isComparisonOperator(got) || 90 | isCombinationOperator(got) || 91 | isNegationOperator(got); 92 | }; 93 | export const isComparisonOperator = (got: any): got is ComparisonOperator => { 94 | return ['==', '*=' , '!=' , '<' , '<=' , '>' , '>=', '<=x<='].includes(got); 95 | }; 96 | export const isCombinationOperator = (got: any): got is CombinationOperator => { 97 | return ['&&', '||'].includes(got); 98 | }; 99 | export const isNegationOperator = (got: any): got is NegationOperator => { 100 | return got === '!'; 101 | }; 102 | 103 | // Filters 104 | export const isFilter = (got: any): got is Filter => { 105 | return isComparisonFilter(got) || 106 | isCombinationFilter(got) || 107 | isGeoStylerBooleanFunction(got) || 108 | isNegationFilter(got) || 109 | isGeoStylerBooleanFunction(got) || 110 | isBoolean(got); 111 | }; 112 | 113 | export const isComparisonFilter = (got: any): got is ComparisonFilter => { 114 | const expectedLength = got && got[0] === '<=x<=' ? 4 : 3; 115 | return ( 116 | Array.isArray(got) && 117 | got.length === expectedLength && 118 | isComparisonOperator(got[0]) && 119 | isExpression(got[1]) && 120 | isExpression(got[2]) && 121 | (got[0] !== '<=x<=' || isNumber(got[3])) 122 | ); 123 | }; 124 | export const isCombinationFilter = (got: any): got is CombinationFilter => { 125 | return Array.isArray(got) && 126 | got.length >= 3 && 127 | isCombinationOperator(got[0]) && 128 | got.every((arg, index) => index === 0 || isFilter(arg)); 129 | }; 130 | export const isNegationFilter = (got: any): got is NegationFilter => { 131 | return Array.isArray(got) && 132 | got.length === 2 && 133 | isNegationOperator(got[0]) && 134 | isFilter(got[1]); 135 | }; 136 | 137 | // Symbolizers 138 | export const isSymbolizer = (got: any): got is Symbolizer => { 139 | return isPointSymbolizer(got) || 140 | isLineSymbolizer(got) || 141 | isFillSymbolizer(got) || 142 | isRasterSymbolizer(got); 143 | }; 144 | export const isPointSymbolizer = (got: any): got is PointSymbolizer => { 145 | return isIconSymbolizer(got) || isMarkSymbolizer(got) || isTextSymbolizer(got); 146 | }; 147 | export const isIconSymbolizer = (got: any): got is IconSymbolizer => { 148 | return got?.kind === 'Icon'; 149 | }; 150 | export const isTextSymbolizer = (got: any): got is TextSymbolizer => { 151 | return got?.kind === 'Text'; 152 | }; 153 | export const isMarkSymbolizer = (got: any): got is MarkSymbolizer => { 154 | return got?.kind === 'Mark' && isString(got?.wellKnownName); 155 | }; 156 | export const isLineSymbolizer = (got: any): got is LineSymbolizer => { 157 | return got?.kind === 'Line'; 158 | }; 159 | export const isFillSymbolizer = (got: any): got is FillSymbolizer => { 160 | return got?.kind === 'Fill'; 161 | }; 162 | export const isRasterSymbolizer = (got: any): got is RasterSymbolizer => { 163 | return got?.kind === 'Raster'; 164 | }; 165 | 166 | // Rule 167 | export const isRule = (got: any): got is Rule => { 168 | return !!(isString(got?.name) && 169 | (got?.filter ? isFilter(got.filter) : true) && 170 | (got?.scaleDenominator ? isScaleDenominator(got.scaleDenominator) : true) && 171 | got?.symbolizers?.every((arg: any) => isSymbolizer(arg))); 172 | }; 173 | 174 | /** 175 | * Checks if ChannelSelection is of type RGBChannel. 176 | */ 177 | export const isRgbChannel = (got: any): got is RGBChannel => { 178 | return !!( 179 | got?.redChannel !== undefined 180 | || got?.greenChannel !== undefined 181 | || got?.blueChannel !== undefined 182 | ); 183 | }; 184 | 185 | /** 186 | * Checks if ChannelSelection is of type GrayChannel. 187 | */ 188 | export const isGrayChannel = (got: any): got is GrayChannel => { 189 | return !!(got?.grayChannel !== undefined); 190 | }; 191 | 192 | // Functions 193 | export const isGeoStylerNumberFunction = (got: any): got is GeoStylerNumberFunction => { 194 | const functionNames: GeoStylerNumberFunction['name'][] = [ 195 | 'abs', 196 | 'acos', 197 | 'add', 198 | 'asin', 199 | 'atan', 200 | 'atan2', 201 | 'ceil', 202 | 'cos', 203 | 'div', 204 | 'exp', 205 | 'floor', 206 | 'interpolate', 207 | 'log', 208 | 'max', 209 | 'min', 210 | 'modulo', 211 | 'mul', 212 | 'pi', 213 | 'pow', 214 | 'random', 215 | 'rint', 216 | 'round', 217 | 'sin', 218 | 'sqrt', 219 | 'strIndexOf', 220 | 'strLastIndexOf', 221 | 'strLength', 222 | 'sub', 223 | 'tan', 224 | 'toDegrees', 225 | 'toNumber', 226 | 'toRadians' 227 | ]; 228 | return functionNames.includes(got?.name); 229 | }; 230 | 231 | export const isGeoStylerStringFunction = (got: any): got is GeoStylerStringFunction => { 232 | const functionNames: GeoStylerStringFunction['name'][] = [ 233 | 'numberFormat', 234 | 'strAbbreviate', 235 | 'strCapitalize', 236 | 'strConcat', 237 | 'strDefaultIfBlank', 238 | 'strReplace', 239 | 'strStripAccents', 240 | 'strSubstring', 241 | 'strSubstringStart', 242 | 'strToLowerCase', 243 | 'strToString', 244 | 'strToUpperCase', 245 | 'strTrim' 246 | ]; 247 | return functionNames.includes(got?.name); 248 | }; 249 | 250 | export const isGeoStylerBooleanFunction = (got: any): got is GeoStylerBooleanFunction => { 251 | const functionNames: GeoStylerBooleanFunction['name'][] = [ 252 | 'all', 253 | 'any', 254 | 'between', 255 | 'double2bool', 256 | 'equalTo', 257 | 'greaterThan', 258 | 'greaterThanOrEqualTo', 259 | 'in', 260 | 'lessThan', 261 | 'lessThanOrEqualTo', 262 | 'not', 263 | 'notEqualTo', 264 | 'parseBoolean', 265 | 'strEndsWith', 266 | 'strEqualsIgnoreCase', 267 | 'strMatches', 268 | 'strStartsWith' 269 | ]; 270 | return functionNames.includes(got?.name); 271 | }; 272 | 273 | export const isGeoStylerUnknownFunction = (got: any): got is GeoStylerUnknownFunction => { 274 | const functionNames: GeoStylerUnknownFunction['name'][] = [ 275 | 'case', 276 | 'property', 277 | 'step' 278 | ]; 279 | return functionNames.includes(got?.name); 280 | }; 281 | 282 | export const isGeoStylerFunction = (got: any): got is GeoStylerFunction => { 283 | return isGeoStylerBooleanFunction(got) || 284 | isGeoStylerNumberFunction(got) || 285 | isGeoStylerStringFunction(got) || 286 | isGeoStylerUnknownFunction(got); 287 | }; 288 | 289 | export const isSprite = (got: any): got is Sprite => { 290 | return typeof got?.source === 'string' || isGeoStylerFunction(got?.source) && 291 | Array.isArray(got.position) && 292 | Array.isArray(got.size); 293 | }; 294 | --------------------------------------------------------------------------------