├── .vscode ├── settings.json └── launch.json ├── bun.lockb ├── .commitlintrc.cjs ├── renovate.json ├── .github ├── FUNDING.yml ├── workflows │ ├── commitlint.yml │ ├── notify-discord.yml │ ├── on-pull-request.yml │ ├── on-push-master.yml │ └── release.yml └── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── vitest.config.ts ├── .gitignore ├── data ├── mapbox │ ├── text_placement.ts │ ├── text_placement_point.ts │ ├── fill_simplefill.ts │ ├── text_placement_line_center.ts │ ├── icon_simpleicon_mapboxapi.ts │ ├── line_simpleline_zoom.ts │ ├── icon_simpleicon.ts │ ├── point_simpletext.ts │ ├── fill_patternfill.ts │ ├── point_placeholderText_simple.ts │ ├── line_simpleline_expression.ts │ ├── line_simpleline.ts │ ├── color_rgba.ts │ ├── fill_graphic_fill_mark.ts │ ├── circle_simplecircle.ts │ ├── point_placeholderText.ts │ ├── icontext_symbolizer.ts │ ├── line_patternline.ts │ ├── expression_get.ts │ ├── fill_simple_outline.ts │ ├── expression_interpolate.ts │ ├── line_simpleline_basefilter.ts │ ├── multi_rule_line_fill.ts │ ├── multi_simpleline_simplefill.ts │ ├── expression_case.ts │ ├── source_layer_mapping.ts │ └── source_mapping.ts ├── styles │ ├── text_placement_line.ts │ ├── text_placement_point.ts │ ├── fill_simplefill.ts │ ├── text_placement_line_center.ts │ ├── point_simpletext.ts │ ├── point_placeholderText.ts │ ├── point_placeholderText_simple.ts │ ├── line_simpleline.ts │ ├── line_simpleline_zoom.ts │ ├── line_simpleline_expression.ts │ ├── circle_simplecircle.ts │ ├── fill_simple_outline.ts │ ├── multi_rule_line_fill.ts │ ├── multi_simpleline_simplefill.ts │ ├── fill_graphic_fill_mark.ts │ ├── gs_expression_lookup.ts │ ├── icon_simpleicon_mapboxapi.ts │ ├── color_rgba.ts │ ├── icon_simpleicon.ts │ ├── gs_expression_property.ts │ ├── line_simpleline_basefilter.ts │ ├── fill_patternfill.ts │ ├── source_layer_mapping.ts │ ├── gs_expression_string.ts │ ├── line_patternline.ts │ ├── source_mapping.ts │ ├── gs_expression_interpolate.ts │ ├── gs_expression_case.ts │ ├── gs_expression_math.ts │ └── gs_expression_decisions.ts ├── mapbox_metadata │ ├── text_placement_line.ts │ ├── text_placement_point.ts │ ├── fill_simplefill.ts │ ├── text_placement_line_center.ts │ ├── icon_simpleicon_mapboxapi.ts │ ├── line_simpleline_zoom.ts │ ├── icon_simpleicon.ts │ ├── point_simpletext.ts │ ├── fill_patternfill.ts │ ├── point_placeholderText_simple.ts │ ├── line_simpleline_expression.ts │ ├── line_simpleline.ts │ ├── circle_simplecircle.ts │ ├── point_placeholderText.ts │ ├── line_patternline.ts │ ├── icontext_symbolizer.ts │ ├── expression_get.ts │ ├── expression_interpolate.ts │ ├── line_simpleline_basefilter.ts │ ├── fill_simple_outline.ts │ ├── expression_lookup.ts │ ├── expression_case.ts │ ├── multi_rule_line_fill.ts │ ├── multi_simpleline_simplefill.ts │ ├── source_layer_mapping.ts │ ├── source_mapping.ts │ ├── expression_string.ts │ ├── expression_decisions.ts │ └── expression_math.ts └── styles_metadata │ └── icontext_symbolizer.ts ├── vite.config.ts ├── tsconfig.test.json ├── tsconfig.json ├── .releaserc ├── eslint.config.mjs ├── LICENSE ├── package.json ├── README.md ├── src ├── Expressions.spec.ts ├── Util │ └── MapboxStyleUtil.ts ├── Expressions.ts └── MapboxStyleParser.spec.ts └── CHANGELOG.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geostyler/geostyler-mapbox-parser/HEAD/bun.lockb -------------------------------------------------------------------------------- /.commitlintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | }; 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | coverage: { 6 | provider: 'istanbul', 7 | reporter: ['text', 'html', 'clover', 'json', 'lcov'] 8 | } 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /.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 | - uses: wagoid/commitlint-github-action@v5 10 | with: 11 | configFile: .commitlintrc.js 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /browser 12 | /dist 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | 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/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 | -------------------------------------------------------------------------------- /data/mapbox/text_placement.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const textPlacement: MbStyle = { 4 | version: 8, 5 | name: 'symbol placement', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Text', 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | layout: { 18 | 'symbol-placement': 'line' 19 | } 20 | } 21 | ] 22 | }; 23 | 24 | export default textPlacement; 25 | -------------------------------------------------------------------------------- /data/mapbox/text_placement_point.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const textPlacementPoint: MbStyle = { 4 | version: 8, 5 | name: 'symbol placement', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Text', 14 | source: 'testsource', 15 | type: 'symbol', 16 | 'source-layer': 'foo', 17 | layout: { 18 | 'symbol-placement': 'point' 19 | } 20 | } 21 | ] 22 | }; 23 | 24 | export default textPlacementPoint; 25 | -------------------------------------------------------------------------------- /data/mapbox/fill_simplefill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillSimpleFill: MbStyle = { 4 | version: 8, 5 | name: 'Simple Fill', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Fill', 14 | type: 'fill', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | paint: { 18 | 'fill-color': '#000000', 19 | 'fill-opacity': 1 20 | } 21 | } 22 | ] 23 | }; 24 | 25 | export default fillSimpleFill; 26 | -------------------------------------------------------------------------------- /data/mapbox/text_placement_line_center.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const textPlacementLineCenter: MbStyle = { 4 | version: 8, 5 | name: 'symbol placement', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Text', 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | layout: { 18 | 'symbol-placement': 'line-center' 19 | } 20 | } 21 | ] 22 | }; 23 | 24 | export default textPlacementLineCenter; 25 | -------------------------------------------------------------------------------- /data/mapbox/icon_simpleicon_mapboxapi.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const iconSimpleIcon: MbStyle = { 4 | version: 8, 5 | name: 'Simple Icon', 6 | sprite: 'mapbox://sprites/mapbox/streets-v8', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'Simple Icon', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'symbol', 18 | layout: { 19 | 'icon-image': 'poi' 20 | } 21 | } 22 | ] 23 | }; 24 | 25 | export default iconSimpleIcon; 26 | -------------------------------------------------------------------------------- /data/mapbox/line_simpleline_zoom.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line Filter', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'Small populated New Yorks', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'line', 16 | minzoom: 5.5, 17 | maxzoom: 10, 18 | paint: { 19 | 'line-color': '#FF0000', 20 | 'line-width': 5 21 | } 22 | }] 23 | }; 24 | 25 | export default lineSimpleLine; 26 | -------------------------------------------------------------------------------- /data/mapbox/icon_simpleicon.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const iconSimpleIcon: MbStyle = { 4 | version: 8, 5 | name: 'Simple Icon', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'Simple Icon', 15 | type: 'symbol', 16 | source: 'testsource', 17 | 'source-layer': 'foo', 18 | layout: { 19 | 'icon-image': 'poi', 20 | 'icon-size': 2 21 | } 22 | } 23 | ] 24 | }; 25 | 26 | export default iconSimpleIcon; 27 | -------------------------------------------------------------------------------- /data/mapbox/point_simpletext.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const pointSimpleText: MbStyle = { 4 | version: 8, 5 | name: 'Simple Text', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Text', 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | layout: { 18 | 'text-field': 'River' 19 | }, 20 | paint: { 21 | 'text-color': '#000000', 22 | 'text-opacity': 1 23 | } 24 | } 25 | ] 26 | }; 27 | 28 | export default pointSimpleText; 29 | -------------------------------------------------------------------------------- /data/styles/text_placement_line.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const textPlacement: Style = { 4 | name: 'symbol placement', 5 | rules: [{ 6 | name: 'Simple Text', 7 | symbolizers: [{ 8 | kind: 'Text', 9 | placement: 'line' 10 | }] 11 | }], 12 | metadata: { 13 | 'mapbox:ref': { 14 | sources: { 15 | testsource: { 16 | type: 'vector' 17 | } 18 | }, 19 | sourceMapping: { 20 | testsource: [0] 21 | }, 22 | sourceLayerMapping: { 23 | foo: [0] 24 | } 25 | } 26 | } 27 | }; 28 | 29 | export default textPlacement; 30 | -------------------------------------------------------------------------------- /data/styles/text_placement_point.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const textPlacementPoint: Style = { 4 | name: 'symbol placement', 5 | rules: [{ 6 | name: 'Simple Text', 7 | symbolizers: [{ 8 | kind: 'Text', 9 | placement: 'point' 10 | }] 11 | }], 12 | metadata: { 13 | 'mapbox:ref': { 14 | sources: { 15 | testsource: { 16 | type: 'vector' 17 | } 18 | }, 19 | sourceMapping: { 20 | testsource: [0] 21 | }, 22 | sourceLayerMapping: { 23 | foo: [0] 24 | } 25 | } 26 | } 27 | }; 28 | 29 | export default textPlacementPoint; 30 | -------------------------------------------------------------------------------- /data/styles/fill_simplefill.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const fillSimpleFill: Style = { 4 | name: 'Simple Fill', 5 | rules: [{ 6 | name: 'Simple Fill', 7 | symbolizers: [{ 8 | kind: 'Fill', 9 | color: '#000000', 10 | opacity: 1 11 | }] 12 | }], 13 | metadata: { 14 | 'mapbox:ref': { 15 | sources: { 16 | testsource: { 17 | type: 'vector' 18 | } 19 | }, 20 | sourceMapping: { 21 | testsource: [0] 22 | }, 23 | sourceLayerMapping: { 24 | foo: [0] 25 | } 26 | } 27 | } 28 | }; 29 | 30 | export default fillSimpleFill; 31 | -------------------------------------------------------------------------------- /data/mapbox/fill_patternfill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillSimpleFill: MbStyle = { 4 | version: 8, 5 | name: 'Pattern Fill', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'Pattern Fill', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'fill', 18 | paint: { 19 | 'fill-color': '#000000', 20 | 'fill-opacity': 1, 21 | 'fill-pattern': 'poi' 22 | } 23 | } 24 | ] 25 | }; 26 | 27 | export default fillSimpleFill; 28 | -------------------------------------------------------------------------------- /data/mapbox/point_placeholderText_simple.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const pointPlaceholderText: MbStyle = { 4 | version: 8, 5 | name: 'Placeholder Text', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Placeholder Text', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'symbol', 17 | layout: { 18 | 'text-field': '{River}' 19 | }, 20 | paint: { 21 | 'text-color': '#000000', 22 | 'text-opacity': 1 23 | } 24 | } 25 | ] 26 | }; 27 | 28 | export default pointPlaceholderText; 29 | -------------------------------------------------------------------------------- /data/styles/text_placement_line_center.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const textPlacementLineCenter: Style = { 4 | name: 'symbol placement', 5 | rules: [{ 6 | name: 'Simple Text', 7 | symbolizers: [{ 8 | kind: 'Text', 9 | placement: 'line-center' 10 | }] 11 | }], 12 | metadata: { 13 | 'mapbox:ref': { 14 | sources: { 15 | testsource: { 16 | type: 'vector' 17 | } 18 | }, 19 | sourceMapping: { 20 | testsource: [0] 21 | }, 22 | sourceLayerMapping: { 23 | foo: [0] 24 | } 25 | } 26 | } 27 | }; 28 | 29 | export default textPlacementLineCenter; 30 | -------------------------------------------------------------------------------- /data/mapbox/line_simpleline_expression.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line Filter', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'Small populated New Yorks', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'line', 16 | paint: { 17 | 'line-color': '#FF0000', 18 | 'line-width': ['case', 19 | ['==', 'DENSITY', 20], 20 | 3, 21 | ['!=', 'DENSITY', 20], 22 | 5 23 | ] 24 | } 25 | }] 26 | }; 27 | 28 | export default lineSimpleLine; 29 | -------------------------------------------------------------------------------- /data/styles/point_simpletext.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const pointSimpleText: Style = { 4 | name: 'Simple Text', 5 | rules: [{ 6 | name: 'Simple Text', 7 | symbolizers: [{ 8 | kind: 'Text', 9 | label: 'River', 10 | color: '#000000', 11 | opacity: 1 12 | }] 13 | }], 14 | metadata: { 15 | 'mapbox:ref': { 16 | sources: { 17 | testsource: { 18 | type: 'vector' 19 | } 20 | }, 21 | sourceMapping: { 22 | testsource: [0] 23 | }, 24 | sourceLayerMapping: { 25 | foo: [0] 26 | } 27 | } 28 | } 29 | }; 30 | 31 | export default pointSimpleText; 32 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | // https://vitejs.dev/config/ 4 | export default defineConfig({ 5 | plugins: [], 6 | build: { 7 | manifest: true, 8 | lib: { 9 | entry: './src/MapboxStyleParser.ts', 10 | name: 'MapboxStyleParser', 11 | formats: ['iife'], 12 | fileName: 'mapboxStyleParser', 13 | }, 14 | rollupOptions: { 15 | output: { 16 | dir: 'dist', 17 | exports: 'named', 18 | generatedCode: 'es5', 19 | format: 'iife', 20 | sourcemap: true 21 | }, 22 | } 23 | }, 24 | define: { 25 | appName: 'GeoStyler' 26 | }, 27 | server: { 28 | host: '0.0.0.0' 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /data/mapbox/line_simpleline.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Line', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'line', 17 | paint: { 18 | 'line-color': '#000000', 19 | 'line-width': 3, 20 | 'line-dasharray': [13, 37] 21 | }, 22 | layout: { 23 | 'line-cap': 'round', 24 | 'line-join': 'miter' 25 | } 26 | } 27 | ] 28 | }; 29 | 30 | export default lineSimpleLine; 31 | -------------------------------------------------------------------------------- /data/mapbox/color_rgba.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const colorRgba: MbStyle = { 4 | version: 8, 5 | name: 'Color RGBA', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Color RGBA', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'circle', 17 | paint: { 18 | 'circle-color': 'rgba(0, 0, 0, 1)', 19 | 'circle-stroke-color': [ 20 | 'case', 21 | ['<', ['get', 'mag'], 2], 22 | 'rgba(255, 0, 0, 1)', 23 | 'rgba(0, 255, 0, 1)' 24 | ] 25 | } 26 | } 27 | ] 28 | }; 29 | 30 | export default colorRgba; 31 | -------------------------------------------------------------------------------- /data/mapbox/fill_graphic_fill_mark.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillGraphicFillMark: MbStyle = { 4 | version: 8, 5 | name: 'Graphic Fill Mark', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | type: 'fill', 14 | paint: { 15 | 'fill-opacity': 0.5, 16 | 'fill-color': '#006C2B' 17 | }, 18 | source: 'testsource', 19 | 'source-layer': 'foo', 20 | id: 'r0_sy0_st0' 21 | } 22 | ], 23 | metadata: { 24 | 'geostyler:ref': { rules: [{name: 'Graphic Fill Mark', symbolizers: [['r0_sy0_st0']]}] }, 25 | } 26 | }; 27 | 28 | export default fillGraphicFillMark; 29 | -------------------------------------------------------------------------------- /data/styles/point_placeholderText.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const pointPlaceholderText: Style = { 4 | name: 'Placeholder Text', 5 | rules: [{ 6 | name: 'Placeholder Text', 7 | symbolizers: [{ 8 | kind: 'Text', 9 | label: 'Area: {{area}}km2', 10 | color: '#000000', 11 | opacity: 1 12 | }] 13 | }], 14 | metadata: { 15 | 'mapbox:ref': { 16 | sources: { 17 | testsource: { 18 | type: 'vector' 19 | } 20 | }, 21 | sourceMapping: { 22 | testsource: [0] 23 | }, 24 | sourceLayerMapping: { 25 | foo: [0] 26 | } 27 | } 28 | } 29 | }; 30 | 31 | export default pointPlaceholderText; 32 | -------------------------------------------------------------------------------- /data/styles/point_placeholderText_simple.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const pointPlaceholderText: Style = { 4 | name: 'Placeholder Text', 5 | rules: [{ 6 | name: 'Placeholder Text', 7 | symbolizers: [{ 8 | kind: 'Text', 9 | label: '{{River}}', 10 | color: '#000000', 11 | opacity: 1 12 | }] 13 | }], 14 | metadata: { 15 | 'mapbox:ref': { 16 | sources: { 17 | testsource: { 18 | type: 'vector' 19 | } 20 | }, 21 | sourceMapping: { 22 | testsource: [0] 23 | }, 24 | sourceLayerMapping: { 25 | foo: [0] 26 | } 27 | } 28 | } 29 | }; 30 | 31 | export default pointPlaceholderText; 32 | -------------------------------------------------------------------------------- /data/styles/line_simpleline.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const lineSimpleLine: Style = { 4 | name: 'Simple Line', 5 | rules: [{ 6 | name: 'Simple Line', 7 | symbolizers: [{ 8 | kind: 'Line', 9 | color: '#000000', 10 | width: 3, 11 | dasharray: [13, 37], 12 | cap: 'round', 13 | join: 'miter' 14 | }] 15 | }], 16 | metadata: { 17 | 'mapbox:ref': { 18 | sources: { 19 | testsource: { 20 | type: 'vector' 21 | } 22 | }, 23 | sourceMapping: { 24 | testsource: [0] 25 | }, 26 | sourceLayerMapping: { 27 | foo: [0] 28 | } 29 | } 30 | } 31 | }; 32 | 33 | export default lineSimpleLine; 34 | -------------------------------------------------------------------------------- /data/mapbox/circle_simplecircle.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const circleSimpleCircle: MbStyle = { 4 | version: 8, 5 | name: 'Simple Circle', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Circle', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'circle', 17 | paint: { 18 | 'circle-color': '#000000', 19 | 'circle-radius': 5, 20 | 'circle-opacity': 1, 21 | 'circle-stroke-width': 2, 22 | 'circle-stroke-color': '#FF0000', 23 | 'circle-stroke-opacity': 0.5 24 | } 25 | } 26 | ] 27 | }; 28 | 29 | export default circleSimpleCircle; 30 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /data/mapbox/point_placeholderText.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const pointPlaceholderText: MbStyle = { 4 | version: 8, 5 | name: 'Placeholder Text', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Placeholder Text', 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | layout: { 18 | 'text-field': ['format', 19 | 'Area: ', {}, 20 | ['get', 'area'], {}, 21 | 'km2', {} 22 | ] 23 | }, 24 | paint: { 25 | 'text-color': '#000000', 26 | 'text-opacity': 1 27 | } 28 | } 29 | ] 30 | }; 31 | 32 | export default pointPlaceholderText; 33 | -------------------------------------------------------------------------------- /data/styles/line_simpleline_zoom.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const lineSimpleLine: Style = { 4 | name: 'Simple Line Filter', 5 | rules: [{ 6 | name: 'Small populated New Yorks', 7 | scaleDenominator: { 8 | min: 545978.7909368654, 9 | max: 12354089.774575673, 10 | }, 11 | symbolizers: [{ 12 | kind: 'Line', 13 | color: '#FF0000', 14 | width: 5 15 | }] 16 | }], 17 | metadata: { 18 | 'mapbox:ref': { 19 | sources: { 20 | testsource: { 21 | type: 'vector' 22 | } 23 | }, 24 | sourceMapping: { 25 | testsource: [0] 26 | }, 27 | sourceLayerMapping: { 28 | foo: [0] 29 | } 30 | } 31 | } 32 | }; 33 | 34 | export default lineSimpleLine; 35 | -------------------------------------------------------------------------------- /data/styles/line_simpleline_expression.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | // TODO: this includes some expressions and should fail for now 4 | const lineSimpleLine: Style = { 5 | name: 'Simple Line Filter', 6 | rules: [{ 7 | name: 'Small populated New Yorks', 8 | symbolizers: [{ 9 | kind: 'Line', 10 | color: '#FF0000', 11 | width: { 12 | name: 'random' 13 | } 14 | }] 15 | }], 16 | metadata: { 17 | 'mapbox:ref': { 18 | sources: { 19 | testsource: { 20 | type: 'vector' 21 | } 22 | }, 23 | sourceMapping: { 24 | testsource: [0] 25 | }, 26 | sourceLayerMapping: { 27 | foo: [0] 28 | } 29 | } 30 | } 31 | }; 32 | 33 | export default lineSimpleLine; 34 | -------------------------------------------------------------------------------- /data/mapbox/icontext_symbolizer.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const iconTextSymbolizer: MbStyle = { 4 | version: 8, 5 | name: 'icontext symbolizer', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | paint: { 18 | 'text-color': 'rgba(45, 45, 45, 1)', 19 | }, 20 | layout: { 21 | 'text-field': '{name}', 22 | 'text-size': 12, 23 | 'icon-image': 'poi', 24 | visibility: 'visible', 25 | }, 26 | id: 'label and icon', 27 | } 28 | ] 29 | }; 30 | 31 | 32 | export default iconTextSymbolizer; 33 | -------------------------------------------------------------------------------- /data/mapbox/line_patternline.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const linePatternLine: MbStyle = { 4 | version: 8, 5 | name: 'Pattern Line', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'Pattern Line', 15 | type: 'line', 16 | source: 'testsource', 17 | 'source-layer': 'foo', 18 | paint: { 19 | 'line-color': '#000000', 20 | 'line-width': 3, 21 | 'line-dasharray': [13, 37], 22 | 'line-pattern': 'poi' 23 | }, 24 | layout: { 25 | 'line-cap': 'round', 26 | 'line-join': 'miter' 27 | } 28 | } 29 | ] 30 | }; 31 | 32 | export default linePatternLine; 33 | -------------------------------------------------------------------------------- /data/mapbox_metadata/text_placement_line.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const textPlacementLine: MbStyle = { 4 | version: 8, 5 | name: 'symbol placement', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'symbol', 17 | layout: { 18 | 'symbol-placement': 'line' 19 | } 20 | } 21 | ], 22 | metadata: { 23 | 'geostyler:ref': { 24 | rules: [{ 25 | name: 'Simple Text', 26 | symbolizers: [ 27 | [ 28 | 'r0_sy0_st0' 29 | ] 30 | ] 31 | }] 32 | } 33 | } 34 | }; 35 | 36 | export default textPlacementLine; 37 | -------------------------------------------------------------------------------- /data/mapbox_metadata/text_placement_point.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const textPlacementPoint: MbStyle = { 4 | version: 8, 5 | name: 'symbol placement', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'symbol', 17 | layout: { 18 | 'symbol-placement': 'point' 19 | } 20 | } 21 | ], 22 | metadata: { 23 | 'geostyler:ref': { 24 | rules: [{ 25 | name: 'Simple Text', 26 | symbolizers: [ 27 | [ 28 | 'r0_sy0_st0' 29 | ] 30 | ] 31 | }] 32 | } 33 | } 34 | }; 35 | 36 | export default textPlacementPoint; 37 | -------------------------------------------------------------------------------- /data/mapbox_metadata/fill_simplefill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillSimpleFill: MbStyle = { 4 | version: 8, 5 | name: 'Simple Fill', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'fill', 17 | paint: { 18 | 'fill-color': '#000000', 19 | 'fill-opacity': 1 20 | } 21 | } 22 | ], 23 | metadata: { 24 | 'geostyler:ref': { 25 | rules: [{ 26 | name: 'Simple Fill', 27 | symbolizers: [ 28 | [ 29 | 'r0_sy0_st0' 30 | ] 31 | ] 32 | }] 33 | } 34 | } 35 | }; 36 | 37 | export default fillSimpleFill; 38 | -------------------------------------------------------------------------------- /data/styles/circle_simplecircle.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const circleSimpleCircle: Style = { 4 | name: 'Simple Circle', 5 | rules: [{ 6 | name: 'Simple Circle', 7 | symbolizers: [{ 8 | kind: 'Mark', 9 | wellKnownName: 'circle', 10 | color: '#000000', 11 | radius: 5, 12 | fillOpacity: 1, 13 | strokeWidth: 2, 14 | strokeColor: '#FF0000', 15 | strokeOpacity: 0.5 16 | }] 17 | }], 18 | metadata: { 19 | 'mapbox:ref': { 20 | sources: { 21 | testsource: { 22 | type: 'vector' 23 | } 24 | }, 25 | sourceMapping: { 26 | testsource: [0] 27 | }, 28 | sourceLayerMapping: { 29 | foo: [0] 30 | } 31 | } 32 | } 33 | }; 34 | 35 | export default circleSimpleCircle; 36 | -------------------------------------------------------------------------------- /data/mapbox_metadata/text_placement_line_center.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const textPlacementLineCenter: MbStyle = { 4 | version: 8, 5 | name: 'symbol placement', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'symbol', 17 | layout: { 18 | 'symbol-placement': 'line-center' 19 | } 20 | } 21 | ], 22 | metadata: { 23 | 'geostyler:ref': { 24 | rules: [{ 25 | name: 'Simple Text', 26 | symbolizers: [ 27 | [ 28 | 'r0_sy0_st0' 29 | ] 30 | ] 31 | }] 32 | } 33 | } 34 | }; 35 | 36 | export default textPlacementLineCenter; 37 | -------------------------------------------------------------------------------- /data/mapbox/expression_get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_get: MbStyle = { 5 | version: 8, 6 | name: 'Expression Get', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'Expression Get', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-color': '#000000', 20 | 'circle-radius': ['get', 'population'], 21 | 'circle-opacity': ['get', 'population_density'], 22 | 'circle-stroke-width': 2, 23 | 'circle-stroke-color': '#FF0000', 24 | 'circle-stroke-opacity': 0.5 25 | } 26 | } 27 | ] 28 | }; 29 | 30 | export default expression_get; 31 | -------------------------------------------------------------------------------- /data/mapbox_metadata/icon_simpleicon_mapboxapi.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const iconSimpleIcon: MbStyle = { 4 | version: 8, 5 | name: 'Simple Icon', 6 | sprite: 'mapbox://sprites/mapbox/streets-v8', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'symbol', 18 | layout: { 19 | 'icon-image': 'poi' 20 | } 21 | } 22 | ], 23 | metadata: { 24 | 'geostyler:ref': { 25 | rules: [{ 26 | name: 'Simple Icon', 27 | symbolizers: [ 28 | [ 29 | 'r0_sy0_st0' 30 | ] 31 | ] 32 | }] 33 | } 34 | } 35 | }; 36 | 37 | export default iconSimpleIcon; 38 | -------------------------------------------------------------------------------- /data/mapbox_metadata/line_simpleline_zoom.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line Filter', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'r0_sy0_st0', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'line', 16 | minzoom: 5.5, 17 | maxzoom: 10, 18 | paint: { 19 | 'line-color': '#FF0000', 20 | 'line-width': 5 21 | } 22 | }], 23 | metadata: { 24 | 'geostyler:ref': { 25 | rules: [{ 26 | name: 'Small populated New Yorks', 27 | symbolizers: [ 28 | [ 29 | 'r0_sy0_st0' 30 | ] 31 | ] 32 | }] 33 | } 34 | } 35 | }; 36 | 37 | export default lineSimpleLine; 38 | -------------------------------------------------------------------------------- /.github/workflows/on-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Test Pull Request 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout sources 11 | uses: actions/checkout@v4 12 | 13 | - name: Set up bun 14 | uses: oven-sh/setup-bun@v2 15 | 16 | - name: Install dependencies ⏬ 17 | run: bun install 18 | 19 | - name: Lint code 💄 20 | run: bun run lint 21 | 22 | - name: Test code ✅ 23 | run: bun test --coverage --coverage-reporter=lcov 24 | 25 | - name: Build artifacts 🏗️ 26 | run: bun run build 27 | 28 | - name: Publish to coveralls ⭐ 29 | # coverage/lcov.info was generated in the previous npm run build step 30 | uses: coverallsapp/github-action@master 31 | with: 32 | github-token: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /tsconfig.test.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 | "exclude": [ 23 | "acceptance-tests", 24 | "browser-build.config.js", 25 | "browser", 26 | "dist", 27 | "coverage", 28 | "data", 29 | "jest", 30 | "node_modules", 31 | "scripts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /data/mapbox_metadata/icon_simpleicon.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const iconSimpleIcon: MbStyle = { 4 | version: 8, 5 | name: 'Simple Icon', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'symbol', 18 | layout: { 19 | 'icon-image': 'poi', 20 | 'icon-size': 2 21 | } 22 | } 23 | ], 24 | metadata: { 25 | 'geostyler:ref': { 26 | rules: [{ 27 | name: 'Simple Icon', 28 | symbolizers: [ 29 | [ 30 | 'r0_sy0_st0' 31 | ] 32 | ] 33 | }] 34 | } 35 | } 36 | }; 37 | 38 | export default iconSimpleIcon; 39 | -------------------------------------------------------------------------------- /data/styles/fill_simple_outline.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const fillSimpleFillOutline: Style = { 4 | name: 'Simple Fill With outline', 5 | rules: [{ 6 | name: 'The name of my rule', 7 | symbolizers: [{ 8 | kind: 'Fill', 9 | color: '#ff0000', 10 | outlineColor: '#00ff00', 11 | outlineWidth: 2, 12 | outlineOpacity: 0.5, 13 | outlineCap: 'butt', 14 | outlineJoin: 'round', 15 | outlineDasharray: [13, 37] 16 | }] 17 | }], 18 | metadata: { 19 | 'mapbox:ref': { 20 | sources: { 21 | testsource: { 22 | type: 'vector' 23 | } 24 | }, 25 | sourceMapping: { 26 | testsource: [0] 27 | }, 28 | sourceLayerMapping: { 29 | foo: [0] 30 | } 31 | } 32 | } 33 | }; 34 | 35 | export default fillSimpleFillOutline; 36 | -------------------------------------------------------------------------------- /.github/workflows/on-push-master.yml: -------------------------------------------------------------------------------- 1 | name: Test Push to Master 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@v4 15 | 16 | - name: Set up bun 17 | uses: oven-sh/setup-bun@v2 18 | 19 | - name: Install dependencies ⏬ 20 | run: bun install 21 | 22 | - name: Lint code 💄 23 | run: bun run lint 24 | 25 | - name: Test code ✅ 26 | run: bun test --coverage --coverage-reporter=lcov 27 | 28 | - name: Build artifacts 🏗️ 29 | run: bun run build 30 | 31 | - name: Publish to coveralls ⭐ 32 | # coverage/lcov.info was generated in the previous npm run build step 33 | uses: coverallsapp/github-action@master 34 | with: 35 | github-token: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /data/mapbox_metadata/point_simpletext.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const pointSimpleText: MbStyle = { 4 | version: 8, 5 | name: 'Simple Text', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'symbol', 17 | layout: { 18 | 'text-field': 'River' 19 | }, 20 | paint: { 21 | 'text-color': '#000000', 22 | 'text-opacity': 1 23 | } 24 | } 25 | ], 26 | metadata: { 27 | 'geostyler:ref': { 28 | rules: [{ 29 | name: 'Simple Text', 30 | symbolizers: [ 31 | [ 32 | 'r0_sy0_st0' 33 | ] 34 | ] 35 | }] 36 | } 37 | } 38 | }; 39 | 40 | export default pointSimpleText; 41 | -------------------------------------------------------------------------------- /.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 | permissions: 12 | contents: write 13 | issues: write 14 | pull-requests: write 15 | id-token: write 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout sources 🔰 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up node 22 | uses: actions/setup-node@v6 23 | with: 24 | node-version: '24' 25 | 26 | - name: Install dependencies ⏬ 27 | run: npm install --force 28 | 29 | - name: Build artifacts 🏗️ 30 | run: npm run build 31 | 32 | - id: semantic-release-step 33 | name: Release with Semantic Release 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | run: npx semantic-release -------------------------------------------------------------------------------- /data/mapbox_metadata/fill_patternfill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillSimpleFill: MbStyle = { 4 | version: 8, 5 | name: 'Pattern Fill', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'fill', 18 | paint: { 19 | 'fill-color': '#000000', 20 | 'fill-opacity': 1, 21 | 'fill-pattern': 'poi' 22 | } 23 | } 24 | ], 25 | metadata: { 26 | 'geostyler:ref': { 27 | rules: [{ 28 | name: 'Pattern Fill', 29 | symbolizers: [ 30 | [ 31 | 'r0_sy0_st0' 32 | ] 33 | ] 34 | }] 35 | } 36 | } 37 | }; 38 | 39 | export default fillSimpleFill; 40 | -------------------------------------------------------------------------------- /data/mapbox/fill_simple_outline.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillSimpleFillOutline: MbStyle = { 4 | version: 8, 5 | name: 'Simple Fill With outline', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'r0_sy0_st0', 13 | type: 'fill', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | paint: { 17 | 'fill-color': '#ff0000' 18 | } 19 | }, 20 | { 21 | id: 'r0_sy0_st1', 22 | type: 'line', 23 | source: 'testsource', 24 | 'source-layer': 'foo', 25 | paint: { 26 | 'line-opacity': 0.5, 27 | 'line-color': '#00ff00', 28 | 'line-width': 2, 29 | 'line-dasharray': [13, 37] 30 | }, 31 | layout: { 32 | 'line-cap': 'butt', 33 | 'line-join': 'round' 34 | } 35 | }] 36 | }; 37 | 38 | export default fillSimpleFillOutline; 39 | -------------------------------------------------------------------------------- /data/mapbox/expression_interpolate.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expressionInterpolate: MbStyle = { 5 | version: 8, 6 | name: 'Expression Interpolate', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'earthquake_circle', 15 | type: 'circle', 16 | source: 'testsource', 17 | 'source-layer': 'foo', 18 | paint: { 19 | 'circle-color': '#000000', 20 | 'circle-opacity': 0.6, 21 | 'circle-radius': [ 22 | 'interpolate', 23 | ['linear'], 24 | ['get', 'population'], 25 | 12, 26 | 2, 27 | 15, 28 | 4, 29 | 19, 30 | 35 31 | ] 32 | } 33 | } 34 | ] 35 | }; 36 | 37 | export default expressionInterpolate; 38 | -------------------------------------------------------------------------------- /data/mapbox_metadata/point_placeholderText_simple.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const pointPlaceholderText: MbStyle = { 4 | version: 8, 5 | name: 'Placeholder Text', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'symbol', 17 | layout: { 18 | 'text-field': '{River}' 19 | }, 20 | paint: { 21 | 'text-color': '#000000', 22 | 'text-opacity': 1 23 | } 24 | } 25 | ], 26 | metadata: { 27 | 'geostyler:ref': { 28 | rules: [{ 29 | name: 'Placeholder Text', 30 | symbolizers: [ 31 | [ 32 | 'r0_sy0_st0' 33 | ] 34 | ] 35 | }] 36 | } 37 | } 38 | }; 39 | 40 | export default pointPlaceholderText; 41 | -------------------------------------------------------------------------------- /data/mapbox_metadata/line_simpleline_expression.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line Filter', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'r0_sy0_st0', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'line', 16 | paint: { 17 | 'line-color': '#FF0000', 18 | 'line-width': ['case', 19 | ['==', 'DENSITY', 20], 20 | 3, 21 | ['!=', 'DENSITY', 20], 22 | 5 23 | ] 24 | } 25 | }], 26 | metadata: { 27 | 'geostyler:ref': { 28 | rules: [{ 29 | name: 'Small populated New Yorks', 30 | symbolizers: [ 31 | [ 32 | 'r0_sy0_st0' 33 | ] 34 | ] 35 | }] 36 | } 37 | } 38 | }; 39 | 40 | export default lineSimpleLine; 41 | -------------------------------------------------------------------------------- /data/styles/multi_rule_line_fill.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const multiRuleLineFill: Style = { 4 | name: 'Rule Line Fill', 5 | rules: [{ 6 | name: 'Line Rule', 7 | symbolizers: [{ 8 | kind: 'Line', 9 | color: '#000000', 10 | width: 3, 11 | dasharray: [13, 37], 12 | cap: 'round', 13 | join: 'miter', 14 | dashOffset: 10 15 | }] 16 | }, { 17 | name: 'Fill Rule', 18 | symbolizers: [{ 19 | kind: 'Fill', 20 | color: '#000000', 21 | opacity: 1 22 | }] 23 | }], 24 | metadata: { 25 | 'mapbox:ref': { 26 | sources: { 27 | testsource: { 28 | type: 'vector' 29 | } 30 | }, 31 | sourceMapping: { 32 | testsource: [0, 1] 33 | }, 34 | sourceLayerMapping: { 35 | foo: [0, 1] 36 | } 37 | } 38 | } 39 | }; 40 | 41 | export default multiRuleLineFill; 42 | -------------------------------------------------------------------------------- /data/mapbox/line_simpleline_basefilter.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Small populated New Yorks', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'Small populated New Yorks', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'line', 16 | filter: ['all', 17 | ['==', 'NAME', 'New York'], 18 | ['==', 'TEST_BOOL', 'true'], 19 | ['==', 'TEST', null], 20 | ['*=', 'TEST2', '*York*'], 21 | ['*=', 'TEST1', '*New*'], 22 | ['!', ['>', 'POPULATION', '100000']], 23 | ['any', 24 | ['==', 'TEST2', '1'], 25 | ['==', 'TEST2', '2'] 26 | ] 27 | ], 28 | paint: { 29 | 'line-color': '#FF0000', 30 | 'line-width': 3 31 | } 32 | }] 33 | }; 34 | 35 | export default lineSimpleLine; 36 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Jest Tests", 6 | "type": "node", 7 | "request": "launch", 8 | "port": 9230, 9 | "runtimeArgs": [ 10 | "--inspect-brk=9230", 11 | "${workspaceRoot}/node_modules/.bin/jest", 12 | "--runInBand", 13 | "--watch" 14 | ], 15 | "runtimeExecutable": null, 16 | "console": "integratedTerminal" 17 | }, 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Debug Jest Tests Windows", 22 | "program": "${workspaceRoot}/node_modules/jest/bin/jest", 23 | "args": [ 24 | "-i", 25 | "--watch" 26 | ], 27 | "internalConsoleOptions": "openOnSessionStart", 28 | "console": "integratedTerminal", 29 | "outFiles": [ 30 | "${workspaceRoot}/build/dist/**/*" 31 | ] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /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": "src", 18 | "sourceMap": true, 19 | "strictNullChecks": true, 20 | "target": "ES2022", 21 | }, 22 | "exclude": [ 23 | "acceptance-tests", 24 | "browser-build.config.js", 25 | "browser", 26 | "dist", 27 | "coverage", 28 | "data", 29 | "jest", 30 | "node_modules", 31 | "scripts", 32 | "src/**.spec.ts", 33 | "vite.config.ts", 34 | "vitest.config.ts" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /data/mapbox/multi_rule_line_fill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const multiRuleLineFill: MbStyle = { 4 | version: 8, 5 | name: 'Rule Line Fill', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Line Rule', 14 | type: 'line', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | paint: { 18 | 'line-color': '#000000', 19 | 'line-width': 3, 20 | 'line-dasharray': [13, 37] 21 | }, 22 | layout: { 23 | 'line-cap': 'round', 24 | 'line-join': 'miter' 25 | } 26 | }, { 27 | id: 'Fill Rule', 28 | type: 'fill', 29 | source: 'testsource', 30 | 'source-layer': 'foo', 31 | paint: { 32 | 'fill-color': '#000000', 33 | 'fill-opacity': 1 34 | } 35 | } 36 | ] 37 | }; 38 | 39 | export default multiRuleLineFill; 40 | -------------------------------------------------------------------------------- /.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 Mapbox Parser" 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 | -------------------------------------------------------------------------------- /data/mapbox_metadata/line_simpleline.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | type: 'line', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | paint: { 18 | 'line-color': '#000000', 19 | 'line-width': 3, 20 | 'line-dasharray': [13, 37] 21 | }, 22 | layout: { 23 | 'line-cap': 'round', 24 | 'line-join': 'miter' 25 | } 26 | } 27 | ], 28 | metadata: { 29 | 'geostyler:ref': { 30 | rules: [{ 31 | name: 'Simple Line', 32 | symbolizers: [ 33 | [ 34 | 'r0_sy0_st0' 35 | ] 36 | ] 37 | }] 38 | } 39 | } 40 | }; 41 | 42 | export default lineSimpleLine; 43 | -------------------------------------------------------------------------------- /data/styles/multi_simpleline_simplefill.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const multiSimpleLineSimpleFill: Style = { 4 | name: 'Simple Line Simple Fill', 5 | rules: [{ 6 | name: 'Simple Line Simple Fill', 7 | symbolizers: [{ 8 | kind: 'Line', 9 | color: '#000000', 10 | width: 3, 11 | dasharray: [13, 37], 12 | cap: 'round', 13 | join: 'miter' 14 | }] 15 | },{ 16 | name: 'Simple Line Simple Fill', 17 | symbolizers: [{ 18 | kind: 'Fill', 19 | color: '#000000', 20 | opacity: 1 21 | }] 22 | }], 23 | metadata: { 24 | 'mapbox:ref': { 25 | sources: { 26 | testsource: { 27 | type: 'vector' 28 | } 29 | }, 30 | sourceMapping: { 31 | testsource: [0, 1] 32 | }, 33 | sourceLayerMapping: { 34 | foo: [0, 1] 35 | } 36 | } 37 | } 38 | }; 39 | 40 | export default multiSimpleLineSimpleFill; 41 | -------------------------------------------------------------------------------- /data/styles/fill_graphic_fill_mark.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const fillGraphicFillMark: Style = { 4 | name: 'Graphic Fill Mark', 5 | rules: [ 6 | { 7 | name: 'Graphic Fill Mark', 8 | symbolizers: [{ 9 | kind: 'Fill', 10 | graphicFill: { 11 | kind: 'Mark', 12 | wellKnownName: 'circle', 13 | opacity: 1, 14 | color: '#006C2B', 15 | rotate: 0, 16 | radius: 0.3, 17 | strokeOpacity: 1, 18 | strokeColor: '#000000', 19 | strokeWidth: 0.2 20 | } 21 | } 22 | ] 23 | }], 24 | metadata: { 25 | 'mapbox:ref': { 26 | sources: { 27 | testsource: { 28 | type: 'vector' 29 | } 30 | }, 31 | sourceMapping: { 32 | testsource: [0] 33 | }, 34 | sourceLayerMapping: { 35 | foo: [0] 36 | } 37 | } 38 | } 39 | }; 40 | 41 | export default fillGraphicFillMark; 42 | -------------------------------------------------------------------------------- /data/mapbox_metadata/circle_simplecircle.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const circleSimpleCircle: MbStyle = { 4 | version: 8, 5 | name: 'Simple Circle', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'circle', 17 | paint: { 18 | 'circle-color': '#000000', 19 | 'circle-radius': 5, 20 | 'circle-opacity': 1, 21 | 'circle-stroke-width': 2, 22 | 'circle-stroke-color': '#FF0000', 23 | 'circle-stroke-opacity': 0.5 24 | } 25 | } 26 | ], 27 | metadata: { 28 | 'geostyler:ref': { 29 | rules: [{ 30 | name: 'Simple Circle', 31 | symbolizers: [ 32 | [ 33 | 'r0_sy0_st0' 34 | ] 35 | ] 36 | }] 37 | } 38 | } 39 | }; 40 | 41 | export default circleSimpleCircle; 42 | -------------------------------------------------------------------------------- /data/mapbox/multi_simpleline_simplefill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const multiSimpleLineSimpleFill: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line Simple Fill', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Line Simple Fill', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'line', 17 | paint: { 18 | 'line-color': '#000000', 19 | 'line-width': 3, 20 | 'line-dasharray': [13, 37] 21 | }, 22 | layout: { 23 | 'line-cap': 'round', 24 | 'line-join': 'miter' 25 | } 26 | }, { 27 | id: 'Simple Line Simple Fill', 28 | source: 'testsource', 29 | 'source-layer': 'foo', 30 | type: 'fill', 31 | paint: { 32 | 'fill-color': '#000000', 33 | 'fill-opacity': 1 34 | } 35 | } 36 | ] 37 | }; 38 | 39 | export default multiSimpleLineSimpleFill; 40 | -------------------------------------------------------------------------------- /data/styles/gs_expression_lookup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gs_expression_lookup: Style = { 5 | name: 'Expression Lookup', 6 | rules: [{ 7 | name: 'length', 8 | symbolizers: [{ 9 | kind: 'Mark', 10 | wellKnownName: 'circle', 11 | radius: { 12 | name: 'strLength', 13 | args: ['peter'] 14 | } 15 | }] 16 | }, { 17 | name: 'slice', 18 | symbolizers: [{ 19 | kind: 'Text', 20 | label: { 21 | name: 'strSubstring', 22 | args: ['peter', 0, 2] 23 | } 24 | }] 25 | }], 26 | metadata: { 27 | 'mapbox:ref': { 28 | sources: { 29 | testsource: { 30 | type: 'vector' 31 | } 32 | }, 33 | sourceMapping: { 34 | testsource: [0, 1] 35 | }, 36 | sourceLayerMapping: { 37 | foo: [0, 1] 38 | } 39 | } 40 | } 41 | }; 42 | 43 | export default gs_expression_lookup; 44 | -------------------------------------------------------------------------------- /data/styles/icon_simpleicon_mapboxapi.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const iconSimpleIcon: Style = { 4 | name: 'Simple Icon', 5 | rules: [{ 6 | name: 'Simple Icon', 7 | symbolizers: [{ 8 | kind: 'Icon', 9 | image: { 10 | source: 'https://api.mapbox.com/sprites/mapbox/streets-v8.png', 11 | position: [0, 0], 12 | size: [12, 12] 13 | } 14 | }] 15 | }], 16 | metadata: { 17 | 'mapbox:ref': { 18 | sources: { 19 | testsource: { 20 | type: 'vector' 21 | } 22 | }, 23 | sourceMapping: { 24 | testsource: [0] 25 | }, 26 | sourceLayerMapping: { 27 | foo: [0] 28 | }, 29 | sprite: { 30 | poi: { 31 | position: [ 32 | 0, 33 | 0, 34 | ], 35 | size: [ 36 | 12, 37 | 12, 38 | ], 39 | }, 40 | }, 41 | } 42 | } 43 | }; 44 | 45 | export default iconSimpleIcon; 46 | -------------------------------------------------------------------------------- /data/mapbox_metadata/point_placeholderText.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const pointPlaceholderText: MbStyle = { 4 | version: 8, 5 | name: 'Placeholder Text', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | layout: { 18 | 'text-field': ['format', 19 | 'Area: ', {}, 20 | ['get', 'area'], {}, 21 | 'km2', {} 22 | ] 23 | }, 24 | paint: { 25 | 'text-color': '#000000', 26 | 'text-opacity': 1 27 | } 28 | } 29 | ], 30 | metadata: { 31 | 'geostyler:ref': { 32 | rules: [{ 33 | name: 'Placeholder Text', 34 | symbolizers: [ 35 | [ 36 | 'r0_sy0_st0' 37 | ] 38 | ] 39 | }] 40 | } 41 | } 42 | }; 43 | 44 | export default pointPlaceholderText; 45 | -------------------------------------------------------------------------------- /data/styles/color_rgba.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const colorRgba: Style = { 4 | name: 'Color RGBA', 5 | rules: [{ 6 | name: 'Color RGBA', 7 | symbolizers: [{ 8 | kind: 'Mark', 9 | wellKnownName: 'circle', 10 | color: '#000000', 11 | strokeColor: { 12 | name: 'case', 13 | args: [ 14 | '#00ff00', { 15 | case: { 16 | name: 'lessThan', 17 | args: [{ 18 | name: 'property', 19 | args: ['mag'] 20 | }, 2] 21 | }, 22 | value: '#ff0000' 23 | } 24 | ] 25 | } 26 | }] 27 | }], 28 | metadata: { 29 | 'mapbox:ref': { 30 | sources: { 31 | testsource: { 32 | type: 'vector' 33 | } 34 | }, 35 | sourceMapping: { 36 | testsource: [0] 37 | }, 38 | sourceLayerMapping: { 39 | foo: [0] 40 | } 41 | } 42 | } 43 | }; 44 | 45 | export default colorRgba; 46 | -------------------------------------------------------------------------------- /data/styles/icon_simpleicon.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const iconSimpleIcon: Style = { 4 | name: 'Simple Icon', 5 | rules: [{ 6 | name: 'Simple Icon', 7 | symbolizers: [{ 8 | kind: 'Icon', 9 | image: { 10 | source: 'https://testurl.com/sprites/mysprite.png', 11 | position: [0, 0], 12 | size: [12, 12] 13 | }, 14 | size: 24 15 | }] 16 | }], 17 | metadata: { 18 | 'mapbox:ref': { 19 | sources: { 20 | testsource: { 21 | type: 'vector' 22 | } 23 | }, 24 | sourceMapping: { 25 | testsource: [0] 26 | }, 27 | sourceLayerMapping: { 28 | foo: [0] 29 | }, 30 | sprite: { 31 | poi: { 32 | 'icon-size': 2, 33 | position: [ 34 | 0, 35 | 0, 36 | ], 37 | size: [ 38 | 12, 39 | 12, 40 | ], 41 | }, 42 | }, 43 | } 44 | } 45 | }; 46 | 47 | export default iconSimpleIcon; 48 | -------------------------------------------------------------------------------- /data/mapbox_metadata/line_patternline.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const linePatternLine: MbStyle = { 4 | version: 8, 5 | name: 'Pattern Line', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | type: 'line', 16 | source: 'testsource', 17 | 'source-layer': 'foo', 18 | paint: { 19 | 'line-color': '#000000', 20 | 'line-width': 3, 21 | 'line-dasharray': [13, 37], 22 | 'line-pattern': 'poi' 23 | }, 24 | layout: { 25 | 'line-cap': 'round', 26 | 'line-join': 'miter' 27 | } 28 | } 29 | ], 30 | metadata: { 31 | 'geostyler:ref': { 32 | rules: [{ 33 | name: 'Pattern Line', 34 | symbolizers: [ 35 | [ 36 | 'r0_sy0_st0' 37 | ] 38 | ] 39 | }] 40 | } 41 | } 42 | }; 43 | 44 | export default linePatternLine; 45 | -------------------------------------------------------------------------------- /data/styles/gs_expression_property.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gs_expression_property: Style = { 5 | name: 'Expression Get', 6 | rules: [{ 7 | name: 'Expression Get', 8 | symbolizers: [{ 9 | kind: 'Mark', 10 | wellKnownName: 'circle', 11 | color: '#000000', 12 | radius: { 13 | name: 'property', 14 | args: ['population'] 15 | }, 16 | fillOpacity: { 17 | name: 'property', 18 | args: ['population_density'] 19 | }, 20 | strokeWidth: 2, 21 | strokeColor: '#FF0000', 22 | strokeOpacity: 0.5 23 | }] 24 | }], 25 | metadata: { 26 | 'mapbox:ref': { 27 | sources: { 28 | testsource: { 29 | type: 'vector' 30 | } 31 | }, 32 | sourceMapping: { 33 | testsource: [0] 34 | }, 35 | sourceLayerMapping: { 36 | foo: [0] 37 | } 38 | } 39 | } 40 | }; 41 | 42 | export default gs_expression_property; 43 | -------------------------------------------------------------------------------- /data/styles/line_simpleline_basefilter.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const lineSimpleLine: Style = { 4 | name: 'Small populated New Yorks', 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 | ], 18 | name: 'Small populated New Yorks', 19 | symbolizers: [{ 20 | kind: 'Line', 21 | color: '#FF0000', 22 | width: 3 23 | }] 24 | }], 25 | metadata: { 26 | 'mapbox:ref': { 27 | sources: { 28 | testsource: { 29 | type: 'vector' 30 | } 31 | }, 32 | sourceMapping: { 33 | testsource: [0] 34 | }, 35 | sourceLayerMapping: { 36 | foo: [0] 37 | } 38 | } 39 | } 40 | }; 41 | 42 | export default lineSimpleLine; 43 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /data/mapbox_metadata/icontext_symbolizer.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const iconTextSymbolizer: MbStyle = { 4 | version: 8, 5 | name: 'icontext symbolizer', 6 | sprite: 'https://testurl.com/sprites/mysprite', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | type: 'symbol', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | paint: { 18 | 'text-color': '#2d2d2d', 19 | }, 20 | layout: { 21 | 'text-field': '{name}', 22 | 'text-size': 12, 23 | 'icon-image': 'poi', 24 | visibility: 'visible', 25 | }, 26 | id: 'r0_sy0_st0', 27 | } 28 | ], 29 | metadata: { 30 | 'geostyler:ref': { 31 | rules: [ 32 | { 33 | name: 'label and icon', 34 | symbolizers: [ 35 | [ 36 | 'r0_sy0_st0', 37 | ] 38 | ], 39 | }, 40 | ], 41 | }, 42 | }, 43 | }; 44 | 45 | export default iconTextSymbolizer; 46 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_get: MbStyle = { 5 | version: 8, 6 | name: 'Expression Get', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-color': '#000000', 20 | 'circle-radius': ['get', 'population'], 21 | 'circle-opacity': ['get', 'population_density'], 22 | 'circle-stroke-width': 2, 23 | 'circle-stroke-color': '#FF0000', 24 | 'circle-stroke-opacity': 0.5 25 | } 26 | } 27 | ], 28 | metadata: { 29 | 'geostyler:ref': { 30 | rules: [{ 31 | name: 'Expression Get', 32 | symbolizers: [ 33 | [ 34 | 'r0_sy0_st0' 35 | ] 36 | ] 37 | }] 38 | } 39 | } 40 | }; 41 | 42 | export default expression_get; 43 | -------------------------------------------------------------------------------- /data/mapbox/expression_case.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_case: MbStyle = { 5 | version: 8, 6 | name: 'Expression Case', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'earthquake_circle', 15 | type: 'circle', 16 | source: 'testsource', 17 | 'source-layer': 'foo', 18 | paint: { 19 | 'circle-color': [ 20 | 'case', 21 | ['<', ['get', 'mag'], 2], 22 | '#fed976', 23 | ['all', ['>=', ['get', 'mag'], 2], ['<', ['get', 'mag'], 3]], 24 | '#feb24c', 25 | ['all', ['>=', ['get', 'mag'], 3], ['<', ['get', 'mag'], 4]], 26 | '#fd8d3c', 27 | ['all', ['>=', ['get', 'mag'], 4], ['<', ['get', 'mag'], 5]], 28 | '#fc4e2a', 29 | '#e31a1c' 30 | ], 31 | 'circle-opacity': 0.6, 32 | 'circle-radius': 12 33 | } 34 | } 35 | ] 36 | }; 37 | 38 | export default expression_case; 39 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import tsParser from '@typescript-eslint/parser'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import js from '@eslint/js'; 5 | import { FlatCompat } from '@eslint/eslintrc'; 6 | 7 | const filename = fileURLToPath(import.meta.url); 8 | const dirname = path.dirname(filename); 9 | const compat = new FlatCompat({ 10 | baseDirectory: dirname, 11 | recommendedConfig: js.configs.recommended, 12 | allConfig: js.configs.all 13 | }); 14 | 15 | export default [ 16 | { 17 | ignores: ['eslint.config.mjs', 'dist/**', 'data/**', '.commitlintrc.cjs'] 18 | }, 19 | ...compat.extends('@terrestris/eslint-config-typescript'), 20 | { 21 | files: ['**/*.ts', '**/*.tsx', '*.mjs'], 22 | languageOptions: { 23 | parser: tsParser, 24 | parserOptions: { 25 | project: [ 26 | 'tsconfig.json', 27 | 'tsconfig.test.json', 28 | ], 29 | tsconfigRootDir: dirname, 30 | } 31 | }, 32 | }, 33 | { 34 | rules: { 35 | '@typescript-eslint/switch-exhaustiveness-check': 'off' 36 | } 37 | } 38 | ]; 39 | -------------------------------------------------------------------------------- /data/styles/fill_patternfill.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const fillPatternFill: Style = { 4 | name: 'Pattern Fill', 5 | rules: [{ 6 | name: 'Pattern Fill', 7 | symbolizers: [{ 8 | kind: 'Fill', 9 | color: '#000000', 10 | opacity: 1, 11 | graphicFill: { 12 | kind: 'Icon', 13 | image: { 14 | source: 'https://testurl.com/sprites/mysprite.png', 15 | position: [0, 0], 16 | size: [12, 12] 17 | } 18 | } 19 | }] 20 | }], 21 | metadata: { 22 | 'mapbox:ref': { 23 | sources: { 24 | testsource: { 25 | type: 'vector' 26 | } 27 | }, 28 | sourceMapping: { 29 | testsource: [0] 30 | }, 31 | sourceLayerMapping: { 32 | foo: [0] 33 | }, 34 | sprite: { 35 | poi: { 36 | position: [ 37 | 0, 38 | 0, 39 | ], 40 | size: [ 41 | 12, 42 | 12, 43 | ], 44 | }, 45 | }, 46 | } 47 | } 48 | }; 49 | 50 | export default fillPatternFill; 51 | -------------------------------------------------------------------------------- /data/mapbox/source_layer_mapping.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const sourceLayerMapping: MbStyle = { 4 | version: 8, 5 | name: 'SourceLayerMapping', 6 | sources: { 7 | first: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'Simple Circle 1', 14 | source: 'first', 15 | 'source-layer': 'foo', 16 | type: 'circle', 17 | paint: { 18 | 'circle-color': '#000000', 19 | 'circle-radius': 5, 20 | 'circle-opacity': 1, 21 | 'circle-stroke-width': 2, 22 | 'circle-stroke-color': '#FF0000', 23 | 'circle-stroke-opacity': 0.5 24 | } 25 | }, 26 | { 27 | id: 'Simple Circle 2', 28 | source: 'first', 29 | 'source-layer': 'bar', 30 | type: 'circle', 31 | paint: { 32 | 'circle-color': '#000000', 33 | 'circle-radius': 5, 34 | 'circle-opacity': 1, 35 | 'circle-stroke-width': 2, 36 | 'circle-stroke-color': '#FF0000', 37 | 'circle-stroke-opacity': 0.5 38 | } 39 | } 40 | ] 41 | }; 42 | 43 | export default sourceLayerMapping; 44 | -------------------------------------------------------------------------------- /data/styles/source_layer_mapping.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const sourceLayerMapping: Style = { 4 | name: 'SourceLayerMapping', 5 | rules: [{ 6 | name: 'Simple Circle 1', 7 | symbolizers: [{ 8 | kind: 'Mark', 9 | wellKnownName: 'circle', 10 | color: '#000000', 11 | radius: 5, 12 | fillOpacity: 1, 13 | strokeWidth: 2, 14 | strokeColor: '#FF0000', 15 | strokeOpacity: 0.5 16 | }] 17 | }, { 18 | name: 'Simple Circle 2', 19 | symbolizers: [{ 20 | kind: 'Mark', 21 | wellKnownName: 'circle', 22 | color: '#000000', 23 | radius: 5, 24 | fillOpacity: 1, 25 | strokeWidth: 2, 26 | strokeColor: '#FF0000', 27 | strokeOpacity: 0.5 28 | }] 29 | }], 30 | metadata: { 31 | 'mapbox:ref': { 32 | sources: { 33 | first: { 34 | type: 'vector' 35 | } 36 | }, 37 | sourceMapping: { 38 | first: [0, 1] 39 | }, 40 | sourceLayerMapping: { 41 | foo: [0], 42 | bar: [1] 43 | } 44 | } 45 | } 46 | }; 47 | 48 | export default sourceLayerMapping; 49 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_interpolate.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_case: MbStyle = { 5 | version: 8, 6 | name: 'Expression Interpolate', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-color': '#000000', 20 | 'circle-opacity': 0.6, 21 | 'circle-radius': [ 22 | 'interpolate', 23 | ['linear'], 24 | ['get', 'population'], 25 | 12, 26 | 2, 27 | 15, 28 | 4, 29 | 19, 30 | 35 31 | ] 32 | } 33 | } 34 | ], 35 | metadata: { 36 | 'geostyler:ref': { 37 | rules: [{ 38 | name: 'earthquake_circle', 39 | symbolizers: [ 40 | [ 41 | 'r0_sy0_st0' 42 | ] 43 | ] 44 | }] 45 | } 46 | } 47 | }; 48 | 49 | export default expression_case; 50 | -------------------------------------------------------------------------------- /data/mapbox_metadata/line_simpleline_basefilter.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const lineSimpleLine: MbStyle = { 4 | version: 8, 5 | name: 'Small populated New Yorks', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'r0_sy0_st0', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'line', 16 | filter: ['all', 17 | ['==', 'NAME', 'New York'], 18 | ['==', 'TEST_BOOL', 'true'], 19 | ['==', 'TEST', null], 20 | ['*=', 'TEST2', '*York*'], 21 | ['*=', 'TEST1', '*New*'], 22 | ['!', ['>', 'POPULATION', '100000']], 23 | ['any', 24 | ['==', 'TEST2', '1'], 25 | ['==', 'TEST2', '2'] 26 | ] 27 | ], 28 | paint: { 29 | 'line-color': '#FF0000', 30 | 'line-width': 3 31 | } 32 | }], 33 | metadata: { 34 | 'geostyler:ref': { 35 | rules: [{ 36 | name: 'Small populated New Yorks', 37 | symbolizers: [ 38 | [ 39 | 'r0_sy0_st0' 40 | ] 41 | ] 42 | }] 43 | } 44 | } 45 | }; 46 | 47 | export default lineSimpleLine; 48 | -------------------------------------------------------------------------------- /data/mapbox/source_mapping.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const sourceMapping: MbStyle = { 4 | version: 8, 5 | name: 'SourceMapping', 6 | sources: { 7 | first: { 8 | type: 'vector' 9 | }, 10 | second: { 11 | type: 'vector' 12 | } 13 | }, 14 | layers: [ 15 | { 16 | id: 'Simple Circle 1', 17 | source: 'first', 18 | 'source-layer': 'foo', 19 | type: 'circle', 20 | paint: { 21 | 'circle-color': '#000000', 22 | 'circle-radius': 5, 23 | 'circle-opacity': 1, 24 | 'circle-stroke-width': 2, 25 | 'circle-stroke-color': '#FF0000', 26 | 'circle-stroke-opacity': 0.5 27 | } 28 | }, 29 | { 30 | id: 'Simple Circle 2', 31 | source: 'second', 32 | 'source-layer': 'foo', 33 | type: 'circle', 34 | paint: { 35 | 'circle-color': '#000000', 36 | 'circle-radius': 5, 37 | 'circle-opacity': 1, 38 | 'circle-stroke-width': 2, 39 | 'circle-stroke-color': '#FF0000', 40 | 'circle-stroke-opacity': 0.5 41 | } 42 | } 43 | ] 44 | }; 45 | 46 | export default sourceMapping; 47 | -------------------------------------------------------------------------------- /data/styles/gs_expression_string.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gs_expression_string: Style = { 5 | name: 'Expression String', 6 | rules: [{ 7 | name: 'concat', 8 | symbolizers: [{ 9 | kind: 'Text', 10 | label: { 11 | name: 'strConcat', 12 | args: ['Lukas', ' ', 'Podolski'] 13 | } 14 | }] 15 | }, { 16 | name: 'downcase', 17 | symbolizers: [{ 18 | kind: 'Text', 19 | label: { 20 | name: 'strToLowerCase', 21 | args: ['Peter'] 22 | } 23 | }] 24 | }, { 25 | name: 'upcase', 26 | symbolizers: [{ 27 | kind: 'Text', 28 | label: { 29 | name: 'strToUpperCase', 30 | args: ['peter'] 31 | } 32 | }] 33 | }], 34 | metadata: { 35 | 'mapbox:ref': { 36 | sources: { 37 | testsource: { 38 | type: 'vector' 39 | } 40 | }, 41 | sourceMapping: { 42 | testsource: [0, 1, 2] 43 | }, 44 | sourceLayerMapping: { 45 | foo: [0, 1, 2] 46 | } 47 | } 48 | } 49 | }; 50 | 51 | export default gs_expression_string; 52 | -------------------------------------------------------------------------------- /data/mapbox_metadata/fill_simple_outline.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const fillSimpleFillOutline: MbStyle = { 4 | version: 8, 5 | name: 'Simple Fill With outline', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [{ 12 | id: 'r0_sy0_st0', 13 | source: 'testsource', 14 | 'source-layer': 'foo', 15 | type: 'fill', 16 | paint: { 17 | 'fill-color': '#ff0000' 18 | } 19 | }, 20 | { 21 | id: 'r0_sy0_st1', 22 | source: 'testsource', 23 | 'source-layer': 'foo', 24 | type: 'line', 25 | paint: { 26 | 'line-opacity': 0.5, 27 | 'line-color': '#00ff00', 28 | 'line-width': 2, 29 | 'line-dasharray': [13, 37] 30 | }, 31 | layout: { 32 | 'line-cap': 'butt', 33 | 'line-join': 'round' 34 | } 35 | }], 36 | metadata: { 37 | 'geostyler:ref': { 38 | rules: [{ 39 | name: 'The name of my rule', 40 | symbolizers: [ 41 | [ 42 | 'r0_sy0_st0', 43 | 'r0_sy0_st1' 44 | ] 45 | ] 46 | }] 47 | } 48 | } 49 | }; 50 | 51 | export default fillSimpleFillOutline; 52 | -------------------------------------------------------------------------------- /data/styles/line_patternline.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const linePatternLine: Style = { 4 | name: 'Pattern Line', 5 | rules: [{ 6 | name: 'Pattern Line', 7 | symbolizers: [{ 8 | kind: 'Line', 9 | color: '#000000', 10 | width: 3, 11 | dasharray: [13, 37], 12 | cap: 'round', 13 | join: 'miter', 14 | graphicFill: { 15 | kind: 'Icon', 16 | image: { 17 | source: 'https://testurl.com/sprites/mysprite.png', 18 | position: [0, 0], 19 | size: [12, 12] 20 | } 21 | } 22 | }] 23 | }], 24 | metadata: { 25 | 'mapbox:ref': { 26 | sources: { 27 | testsource: { 28 | type: 'vector' 29 | } 30 | }, 31 | sourceMapping: { 32 | testsource: [0] 33 | }, 34 | sourceLayerMapping: { 35 | foo: [0] 36 | }, 37 | sprite: { 38 | poi: { 39 | position: [ 40 | 0, 41 | 0, 42 | ], 43 | size: [ 44 | 12, 45 | 12, 46 | ], 47 | }, 48 | }, 49 | } 50 | } 51 | }; 52 | 53 | export default linePatternLine; 54 | -------------------------------------------------------------------------------- /data/styles/source_mapping.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const sourceMapping: Style = { 4 | name: 'SourceMapping', 5 | rules: [{ 6 | name: 'Simple Circle 1', 7 | symbolizers: [{ 8 | kind: 'Mark', 9 | wellKnownName: 'circle', 10 | color: '#000000', 11 | radius: 5, 12 | fillOpacity: 1, 13 | strokeWidth: 2, 14 | strokeColor: '#FF0000', 15 | strokeOpacity: 0.5 16 | }] 17 | }, { 18 | name: 'Simple Circle 2', 19 | symbolizers: [{ 20 | kind: 'Mark', 21 | wellKnownName: 'circle', 22 | color: '#000000', 23 | radius: 5, 24 | fillOpacity: 1, 25 | strokeWidth: 2, 26 | strokeColor: '#FF0000', 27 | strokeOpacity: 0.5 28 | }] 29 | }], 30 | metadata: { 31 | 'mapbox:ref': { 32 | sources: { 33 | first: { 34 | type: 'vector' 35 | }, 36 | second: { 37 | type: 'vector' 38 | } 39 | }, 40 | sourceMapping: { 41 | first: [0], 42 | second: [1] 43 | }, 44 | sourceLayerMapping: { 45 | foo: [0, 1] 46 | } 47 | } 48 | } 49 | }; 50 | 51 | export default sourceMapping; 52 | -------------------------------------------------------------------------------- /data/styles/gs_expression_interpolate.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gsExpressionInterpolate: Style = { 5 | name: 'Expression Interpolate', 6 | rules: [{ 7 | name: 'earthquake_circle', 8 | symbolizers: [{ 9 | kind: 'Mark', 10 | wellKnownName: 'circle', 11 | color: '#000000', 12 | fillOpacity: 0.6, 13 | radius: { 14 | name: 'interpolate', 15 | args: [{ 16 | name: 'linear' 17 | }, { 18 | name: 'property', 19 | args: ['population'] 20 | }, { 21 | stop: 12, 22 | value: 2 23 | }, { 24 | stop: 15, 25 | value: 4 26 | }, { 27 | stop: 19, 28 | value: 35 29 | }] 30 | } 31 | }] 32 | }], 33 | metadata: { 34 | 'mapbox:ref': { 35 | sources: { 36 | testsource: { 37 | type: 'vector' 38 | } 39 | }, 40 | sourceMapping: { 41 | testsource: [0] 42 | }, 43 | sourceLayerMapping: { 44 | foo: [0] 45 | } 46 | } 47 | } 48 | }; 49 | 50 | export default gsExpressionInterpolate; 51 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_lookup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_lookup: MbStyle = { 5 | version: 8, 6 | name: 'Expression Lookup', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-radius': ['length', 'peter'] 20 | } 21 | }, 22 | { 23 | id: 'r1_sy0_st0', 24 | source: 'testsource', 25 | 'source-layer': 'foo', 26 | type: 'symbol', 27 | layout: { 28 | 'text-field': ['slice', 'peter', 0, 2] 29 | } 30 | } 31 | ], 32 | metadata: { 33 | 'geostyler:ref': { 34 | rules: [{ 35 | name: 'length', 36 | symbolizers: [ 37 | [ 38 | 'r0_sy0_st0' 39 | ] 40 | ] 41 | }, { 42 | name: 'slice', 43 | symbolizers: [ 44 | [ 45 | 'r1_sy0_st0' 46 | ] 47 | ] 48 | }] 49 | } 50 | } 51 | }; 52 | 53 | export default expression_lookup; 54 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_case.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_case: MbStyle = { 5 | version: 8, 6 | name: 'Expression Case', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-color': [ 20 | 'case', 21 | ['<', ['get', 'mag'], 2], 22 | '#fed976', 23 | ['all', ['>=', ['get', 'mag'], 2], ['<', ['get', 'mag'], 3]], 24 | '#feb24c', 25 | ['all', ['>=', ['get', 'mag'], 3], ['<', ['get', 'mag'], 4]], 26 | '#fd8d3c', 27 | ['all', ['>=', ['get', 'mag'], 4], ['<', ['get', 'mag'], 5]], 28 | '#fc4e2a', 29 | '#e31a1c' 30 | ], 31 | 'circle-opacity': 0.6, 32 | 'circle-radius': 12 33 | } 34 | } 35 | ], 36 | metadata: { 37 | 'geostyler:ref': { 38 | rules: [{ 39 | name: 'earthquake_circle', 40 | symbolizers: [ 41 | [ 42 | 'r0_sy0_st0' 43 | ] 44 | ] 45 | }] 46 | } 47 | } 48 | }; 49 | 50 | export default expression_case; 51 | -------------------------------------------------------------------------------- /data/mapbox_metadata/multi_rule_line_fill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const multiRuleLineFill: MbStyle = { 4 | version: 8, 5 | name: 'Rule Line Fill', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'line', 17 | paint: { 18 | 'line-color': '#000000', 19 | 'line-width': 3, 20 | 'line-dasharray': [13, 37] 21 | }, 22 | layout: { 23 | 'line-cap': 'round', 24 | 'line-join': 'miter' 25 | } 26 | }, { 27 | id: 'r1_sy0_st0', 28 | source: 'testsource', 29 | 'source-layer': 'foo', 30 | type: 'fill', 31 | paint: { 32 | 'fill-color': '#000000', 33 | 'fill-opacity': 1 34 | } 35 | } 36 | ], 37 | metadata: { 38 | 'geostyler:ref': { 39 | rules: [{ 40 | name: 'Line Rule', 41 | symbolizers: [ 42 | [ 43 | 'r0_sy0_st0' 44 | ] 45 | ] 46 | },{ 47 | name: 'Fill Rule', 48 | symbolizers: [ 49 | [ 50 | 'r1_sy0_st0' 51 | ] 52 | ] 53 | }] 54 | } 55 | } 56 | }; 57 | 58 | export default multiRuleLineFill; 59 | -------------------------------------------------------------------------------- /data/mapbox_metadata/multi_simpleline_simplefill.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const multiSimpleLineSimpleFill: MbStyle = { 4 | version: 8, 5 | name: 'Simple Line Simple Fill', 6 | sources: { 7 | testsource: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'testsource', 15 | 'source-layer': 'foo', 16 | type: 'line', 17 | paint: { 18 | 'line-color': '#000000', 19 | 'line-width': 3, 20 | 'line-dasharray': [13, 37] 21 | }, 22 | layout: { 23 | 'line-cap': 'round', 24 | 'line-join': 'miter' 25 | } 26 | }, { 27 | id: 'r1_sy0_st0', 28 | source: 'testsource', 29 | 'source-layer': 'foo', 30 | type: 'fill', 31 | paint: { 32 | 'fill-color': '#000000', 33 | 'fill-opacity': 1 34 | } 35 | } 36 | ], 37 | metadata: { 38 | 'geostyler:ref': { 39 | rules: [{ 40 | name: 'Simple Line Simple Fill', 41 | symbolizers: [ 42 | [ 43 | 'r0_sy0_st0' 44 | ] 45 | ] 46 | },{ 47 | name: 'Simple Line Simple Fill', 48 | symbolizers: [ 49 | [ 50 | 'r1_sy0_st0' 51 | ] 52 | ] 53 | }] 54 | } 55 | } 56 | }; 57 | 58 | export default multiSimpleLineSimpleFill; 59 | -------------------------------------------------------------------------------- /data/styles_metadata/icontext_symbolizer.ts: -------------------------------------------------------------------------------- 1 | import { Style } from 'geostyler-style'; 2 | 3 | const iconTextSymbolizer: Style = { 4 | name: 'icontext symbolizer', 5 | rules: [ 6 | { 7 | name: 'label and icon', 8 | symbolizers: [ 9 | { 10 | kind: 'Text', 11 | color: '#2d2d2d', 12 | label: '{{name}}', 13 | size: 12, 14 | visibility: true 15 | }, 16 | { 17 | kind: 'Icon', 18 | visibility: true, 19 | image: { 20 | source: 'https://testurl.com/sprites/mysprite.png', 21 | position: [0, 0], 22 | size: [12, 12] 23 | } 24 | } 25 | ] 26 | } 27 | ], 28 | metadata: { 29 | 'mapbox:ref': { 30 | sources: { 31 | testsource: { 32 | type: 'vector' 33 | } 34 | }, 35 | splitSymbolizers: [{ 36 | rule: 0, 37 | symbolizers: [0, 1] 38 | }], 39 | sourceMapping: { 40 | testsource: [0] 41 | }, 42 | sourceLayerMapping: { 43 | foo: [0] 44 | }, 45 | sprite: { 46 | poi: { 47 | position: [ 48 | 0, 49 | 0, 50 | ], 51 | size: [ 52 | 12, 53 | 12, 54 | ], 55 | }, 56 | }, 57 | } 58 | } 59 | }; 60 | 61 | export default iconTextSymbolizer; 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, terrestris GmbH & Co. KG 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 | -------------------------------------------------------------------------------- /data/mapbox_metadata/source_layer_mapping.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const sourceLayerMapping: MbStyle = { 4 | version: 8, 5 | name: 'SourceLayerMapping', 6 | sources: { 7 | first: { 8 | type: 'vector' 9 | } 10 | }, 11 | layers: [ 12 | { 13 | id: 'r0_sy0_st0', 14 | source: 'first', 15 | 'source-layer': 'foo', 16 | type: 'circle', 17 | paint: { 18 | 'circle-color': '#000000', 19 | 'circle-radius': 5, 20 | 'circle-opacity': 1, 21 | 'circle-stroke-width': 2, 22 | 'circle-stroke-color': '#FF0000', 23 | 'circle-stroke-opacity': 0.5 24 | } 25 | }, 26 | { 27 | id: 'r1_sy0_st0', 28 | source: 'first', 29 | 'source-layer': 'bar', 30 | type: 'circle', 31 | paint: { 32 | 'circle-color': '#000000', 33 | 'circle-radius': 5, 34 | 'circle-opacity': 1, 35 | 'circle-stroke-width': 2, 36 | 'circle-stroke-color': '#FF0000', 37 | 'circle-stroke-opacity': 0.5 38 | } 39 | } 40 | ], 41 | metadata: { 42 | 'geostyler:ref': { 43 | rules: [{ 44 | name: 'Simple Circle 1', 45 | symbolizers: [[ 46 | 'r0_sy0_st0' 47 | ]] 48 | }, { 49 | name: 'Simple Circle 2', 50 | symbolizers: [[ 51 | 'r1_sy0_st0' 52 | ]] 53 | }] 54 | } 55 | } 56 | }; 57 | 58 | export default sourceLayerMapping; 59 | -------------------------------------------------------------------------------- /data/mapbox_metadata/source_mapping.ts: -------------------------------------------------------------------------------- 1 | import { MbStyle } from '../../src/MapboxStyleParser'; 2 | 3 | const sourceMapping: MbStyle = { 4 | version: 8, 5 | name: 'SourceMapping', 6 | sources: { 7 | first: { 8 | type: 'vector' 9 | }, 10 | second: { 11 | type: 'vector' 12 | } 13 | }, 14 | layers: [ 15 | { 16 | id: 'r0_sy0_st0', 17 | source: 'first', 18 | 'source-layer': 'foo', 19 | type: 'circle', 20 | paint: { 21 | 'circle-color': '#000000', 22 | 'circle-radius': 5, 23 | 'circle-opacity': 1, 24 | 'circle-stroke-width': 2, 25 | 'circle-stroke-color': '#FF0000', 26 | 'circle-stroke-opacity': 0.5 27 | } 28 | }, 29 | { 30 | id: 'r1_sy0_st0', 31 | source: 'second', 32 | 'source-layer': 'foo', 33 | type: 'circle', 34 | paint: { 35 | 'circle-color': '#000000', 36 | 'circle-radius': 5, 37 | 'circle-opacity': 1, 38 | 'circle-stroke-width': 2, 39 | 'circle-stroke-color': '#FF0000', 40 | 'circle-stroke-opacity': 0.5 41 | } 42 | } 43 | ], 44 | metadata: { 45 | 'geostyler:ref': { 46 | rules: [{ 47 | name: 'Simple Circle 1', 48 | symbolizers: [[ 49 | 'r0_sy0_st0' 50 | ]] 51 | }, { 52 | name: 'Simple Circle 2', 53 | symbolizers: [[ 54 | 'r1_sy0_st0' 55 | ]] 56 | }] 57 | } 58 | } 59 | }; 60 | 61 | export default sourceMapping; 62 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_string.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_string: MbStyle = { 5 | version: 8, 6 | name: 'Expression String', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'symbol', 18 | layout: { 19 | 'text-field': [ 20 | 'concat', 21 | 'Lukas', 22 | ' ', 23 | 'Podolski' 24 | ] 25 | } 26 | }, { 27 | id: 'r1_sy0_st0', 28 | source: 'testsource', 29 | 'source-layer': 'foo', 30 | type: 'symbol', 31 | layout: { 32 | 'text-field': [ 33 | 'downcase', 34 | 'Peter' 35 | ] 36 | } 37 | }, { 38 | id: 'r2_sy0_st0', 39 | source: 'testsource', 40 | 'source-layer': 'foo', 41 | type: 'symbol', 42 | layout: { 43 | 'text-field': [ 44 | 'upcase', 45 | 'peter' 46 | ] 47 | } 48 | } 49 | ], 50 | metadata: { 51 | 'geostyler:ref': { 52 | rules: [{ 53 | name: 'concat', 54 | symbolizers: [ 55 | [ 56 | 'r0_sy0_st0' 57 | ] 58 | ] 59 | }, { 60 | name: 'downcase', 61 | symbolizers: [ 62 | [ 63 | 'r1_sy0_st0' 64 | ] 65 | ] 66 | }, { 67 | name: 'upcase', 68 | symbolizers: [ 69 | [ 70 | 'r2_sy0_st0' 71 | ] 72 | ] 73 | }] 74 | } 75 | } 76 | }; 77 | 78 | export default expression_string; 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geostyler-mapbox-parser", 3 | "version": "6.2.0", 4 | "description": "GeoStyler-Style-Parser implementation for Mapbox", 5 | "type": "module", 6 | "main": "dist/MapboxStyleParser.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/geostyler/geostyler-mapbox-parser.git" 10 | }, 11 | "files": [ 12 | "dist" 13 | ], 14 | "keywords": [ 15 | "mapbox", 16 | "geostyler" 17 | ], 18 | "author": "", 19 | "license": "BSD-2-Clause", 20 | "bugs": { 21 | "url": "https://github.com/geostyler/geostyler-mapbox-parser/issues" 22 | }, 23 | "homepage": "https://github.com/geostyler/geostyler-mapbox-parser#readme", 24 | "engines": { 25 | "node": ">=18", 26 | "npm": ">=9" 27 | }, 28 | "dependencies": { 29 | "@types/mapbox-gl": "^3.4.1", 30 | "geostyler-style": "^10.5.0" 31 | }, 32 | "scripts": { 33 | "build-browser": "vite build", 34 | "build-dist": "tsc", 35 | "build": "npm run build-browser && npm run build-dist", 36 | "lint:test:build": "npm run lint && npm run test && npm run build", 37 | "lint:test": "npm run lint && npm run test", 38 | "lint": "eslint -c eslint.config.mjs --ext .ts . && tsc --noEmit", 39 | "prepublishOnly": "npm run build", 40 | "prepare": "npm run build", 41 | "test-watch": "NODE_OPTIONS=--import=extensionless/register vitest", 42 | "test": "NODE_OPTIONS=--import=extensionless/register vitest --coverage run" 43 | }, 44 | "devDependencies": { 45 | "@babel/polyfill": "^7.12.1", 46 | "@commitlint/cli": "^19.8.1", 47 | "@commitlint/config-conventional": "^19.8.1", 48 | "@semantic-release/changelog": "^6.0.3", 49 | "@semantic-release/git": "^10.0.1", 50 | "@stylistic/eslint-plugin": "^4.4.1", 51 | "@terrestris/eslint-config-typescript": "^9.0.0", 52 | "@types/lodash": "^4.17.17", 53 | "@types/node": "^22.15.29", 54 | "@typescript-eslint/eslint-plugin": "^8.33.1", 55 | "@typescript-eslint/eslint-plugin-tslint": "^7.0.2", 56 | "@typescript-eslint/parser": "^8.33.1", 57 | "@vitest/coverage-istanbul": "^3.2.1", 58 | "conventional-changelog-conventionalcommits": "^9.0.0", 59 | "eslint": "^9.28.0", 60 | "extensionless": "^1.9.9", 61 | "jest": "^29.7.0", 62 | "lodash": "^4.17.21", 63 | "semantic-release": "^25.0.2", 64 | "typescript": "^5.8.3", 65 | "vite": "^6.3.5", 66 | "vitest": "^3.2.1" 67 | }, 68 | "funding": "https://opencollective.com/geostyler" 69 | } 70 | -------------------------------------------------------------------------------- /data/styles/gs_expression_case.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gs_expression_case: Style = { 5 | name: 'Expression Case', 6 | rules: [{ 7 | name: 'earthquake_circle', 8 | symbolizers: [{ 9 | kind: 'Mark', 10 | wellKnownName: 'circle', 11 | color: { 12 | name: 'case', 13 | args: ['#e31a1c', { 14 | case: { 15 | name: 'lessThan', 16 | args: [{ 17 | name: 'property', 18 | args: ['mag'] 19 | }, 20 | 2] 21 | }, 22 | value: '#fed976' 23 | }, { 24 | case: { 25 | name: 'all', 26 | args: [{ 27 | name: 'greaterThanOrEqualTo', 28 | args: [{ 29 | name: 'property', 30 | args: ['mag'] 31 | }, 32 | 2] 33 | }, { 34 | name: 'lessThan', 35 | args: [{ 36 | name: 'property', 37 | args: ['mag'] 38 | }, 39 | 3] 40 | }] 41 | }, 42 | value:'#feb24c' 43 | }, { 44 | case: { 45 | name: 'all', 46 | args: [{ 47 | name: 'greaterThanOrEqualTo', 48 | args: [{ 49 | name: 'property', 50 | args: ['mag'] 51 | }, 52 | 3] 53 | }, { 54 | name: 'lessThan', 55 | args: [{ 56 | name: 'property', 57 | args: ['mag'] 58 | }, 59 | 4] 60 | }] 61 | }, 62 | value:'#fd8d3c' 63 | }, { 64 | case: { 65 | name: 'all', 66 | args: [{ 67 | name: 'greaterThanOrEqualTo', 68 | args: [{ 69 | name: 'property', 70 | args: ['mag'] 71 | }, 72 | 4] 73 | }, { 74 | name: 'lessThan', 75 | args: [{ 76 | name: 'property', 77 | args: ['mag'] 78 | }, 79 | 5] 80 | }] 81 | }, 82 | value:'#fc4e2a' 83 | }] 84 | }, 85 | radius: 12, 86 | fillOpacity: 0.6 87 | }] 88 | }], 89 | metadata: { 90 | 'mapbox:ref': { 91 | sources: { 92 | testsource: { 93 | type: 'vector' 94 | } 95 | }, 96 | sourceMapping: { 97 | testsource: [0] 98 | }, 99 | sourceLayerMapping: { 100 | foo: [0] 101 | } 102 | } 103 | } 104 | }; 105 | 106 | export default gs_expression_case; 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geostyler-mapbox-parser 2 | 3 | [![Coverage Status](https://coveralls.io/repos/github/geostyler/geostyler-mapbox-parser/badge.svg?branch=master)](https://coveralls.io/github/geostyler/geostyler-mapbox-parser?branch=master) 4 | [![License](https://img.shields.io/github/license/geostyler/geostyler-mapbox-parser)](https://github.com/geostyler/geostyler-mapbox-parser/blob/master/LICENSE) 5 | [![npm version](https://badge.fury.io/js/geostyler-mapbox-parser.svg)](https://www.npmjs.com/package/geostyler-mapbox-parser) 6 | 7 | GeoStyler-Style-Parser implementation for Mapbox 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 | ### Important Notes 14 | Since mapbox works with [spritesheets](https://docs.mapbox.com/api/maps/#sprites), geostyler-mapbox-parser is only capable of handling sprites/icons if the application that is using the parser implements following API: 15 | 16 | `GET /sprites/?name&baseurl` 17 | - `name` - name of the sprite in the spritesheet corresponding json 18 | - `baseurl` - baseurl for retrieving spritesheet and sprite json 19 | 20 | The endpoint MUST return a reference to a single image. 21 | 22 | --- 23 | 24 | Mapbox styles require the properties [`sources`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#root-sources) (root property) 25 | and [`source`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layer-source) (layers property). 26 | In order to keep a clear separation between style and data, geostyler-mapbox-parser puts the source-related attributes into the metadata block of 27 | a geostyler-style under `mapbox:ref`. There, the original sources object, as well as the mappings between layers and sources will be stored 28 | (properties `sources`, `sourceMapping` and `layerSourceMapping`). Applications can then use these mappings for re-creating the same layer structure 29 | using their map library of choice (e.g. OpenLayers, etc.). 30 | 31 | ### How to use 32 | 33 | ES6: 34 | ```js 35 | import MapboxParser from "geostyler-mapbox-parser"; 36 | 37 | const pointSimplePoint = { 38 | name: "My Style", 39 | rules: [ 40 | { 41 | name: "My Rule", 42 | symbolizers: [ 43 | { 44 | kind: "Mark", 45 | wellKnownName: "circle", 46 | color: "#FF0000", 47 | radius: 6 48 | } 49 | ] 50 | } 51 | ] 52 | }; 53 | 54 | const parser = new MapboxParser(); 55 | 56 | const { output: mbStyle } = await parser.writeStyle(pointSimplePoint); 57 | console.log(mbStyle); 58 | ``` 59 | 60 | Browser: 61 | 62 | ```js 63 | const pointSimplePoint = { 64 | name: "My Style", 65 | rules: [ 66 | { 67 | name: "My Rule", 68 | symbolizers: [ 69 | { 70 | kind: "Mark", 71 | wellKnownName: "Circle", 72 | color: "#FF0000", 73 | radius: 6 74 | } 75 | ] 76 | } 77 | ] 78 | }; 79 | var parser = new GeoStylerMapboxParser.MapboxStyleParser(); 80 | parser 81 | .writeStyle(pointSimplePoint) 82 | .then(function(res) { 83 | var mbStyle = res.output; 84 | console.log(mbStyle); 85 | }); 86 | ``` 87 | 88 | ## Funding & financial sponsorship 89 | 90 | Maintenance and further development of this code can be funded through the 91 | [GeoStyler Open Collective](https://opencollective.com/geostyler). All contributions and 92 | expenses can transparently be reviewed by anyone; you see what we use the donated money for. 93 | Thank you for any financial support you give the GeoStyler project 💞 94 | 95 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_decisions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_decisions: MbStyle = { 5 | version: 8, 6 | name: 'Expression Decisions', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-color': [ 20 | 'case', 21 | ['!', ['==', ['get', 'mag'], 1]], 22 | '#FFFFFF', 23 | '#000000' 24 | ] 25 | } 26 | }, 27 | { 28 | id: 'r1_sy0_st0', 29 | source: 'testsource', 30 | 'source-layer': 'foo', 31 | type: 'circle', 32 | paint: { 33 | 'circle-color': [ 34 | 'case', 35 | ['!=', ['get', 'mag'], 1], 36 | '#FFFFFF', 37 | '#000000' 38 | ] 39 | } 40 | }, 41 | { 42 | id: 'r2_sy0_st0', 43 | source: 'testsource', 44 | 'source-layer': 'foo', 45 | type: 'circle', 46 | paint: { 47 | 'circle-color': [ 48 | 'case', 49 | ['<', ['get', 'mag'], 1], 50 | '#FFFFFF', 51 | '#000000' 52 | ] 53 | } 54 | }, 55 | { 56 | id: 'r3_sy0_st0', 57 | source: 'testsource', 58 | 'source-layer': 'foo', 59 | type: 'circle', 60 | paint: { 61 | 'circle-color': [ 62 | 'case', 63 | ['<=', ['get', 'mag'], 1], 64 | '#FFFFFF', 65 | '#000000' 66 | ] 67 | } 68 | }, 69 | { 70 | id: 'r4_sy0_st0', 71 | source: 'testsource', 72 | 'source-layer': 'foo', 73 | type: 'circle', 74 | paint: { 75 | 'circle-color': [ 76 | 'case', 77 | ['==', ['get', 'mag'], 1], 78 | '#FFFFFF', 79 | '#000000' 80 | ] 81 | } 82 | }, 83 | { 84 | id: 'r5_sy0_st0', 85 | source: 'testsource', 86 | 'source-layer': 'foo', 87 | type: 'circle', 88 | paint: { 89 | 'circle-color': [ 90 | 'case', 91 | ['>', ['get', 'mag'], 1], 92 | '#FFFFFF', 93 | '#000000' 94 | ] 95 | } 96 | }, 97 | { 98 | id: 'r6_sy0_st0', 99 | source: 'testsource', 100 | 'source-layer': 'foo', 101 | type: 'circle', 102 | paint: { 103 | 'circle-color': [ 104 | 'case', 105 | ['>=', ['get', 'mag'], 1], 106 | '#FFFFFF', 107 | '#000000' 108 | ] 109 | } 110 | }, 111 | { 112 | id: 'r7_sy0_st0', 113 | source: 'testsource', 114 | 'source-layer': 'foo', 115 | type: 'circle', 116 | paint: { 117 | 'circle-color': [ 118 | 'case', 119 | ['all', 120 | ['>=', ['get', 'mag'], 1], 121 | ['<=', ['get', 'mag'], 2] 122 | ], 123 | '#FFFFFF', 124 | '#000000' 125 | ] 126 | } 127 | }, 128 | { 129 | id: 'r8_sy0_st0', 130 | source: 'testsource', 131 | 'source-layer': 'foo', 132 | type: 'circle', 133 | paint: { 134 | 'circle-color': [ 135 | 'case', 136 | ['any', 137 | ['==', ['get', 'mag'], 'a'], 138 | ['==', ['get', 'mag'], 'b'] 139 | ], 140 | '#FFFFFF', 141 | '#000000' 142 | ] 143 | } 144 | } 145 | ], 146 | metadata: { 147 | 'geostyler:ref': { 148 | rules: [{ 149 | name: 'negation', 150 | symbolizers: [ 151 | [ 152 | 'r0_sy0_st0' 153 | ] 154 | ] 155 | }, { 156 | name: 'not equal to', 157 | symbolizers: [ 158 | [ 159 | 'r1_sy0_st0' 160 | ] 161 | ] 162 | }, { 163 | name: 'less than', 164 | symbolizers: [ 165 | [ 166 | 'r2_sy0_st0' 167 | ] 168 | ] 169 | }, { 170 | name: 'less than or equal to', 171 | symbolizers: [ 172 | [ 173 | 'r3_sy0_st0' 174 | ] 175 | ] 176 | }, { 177 | name: 'equal to', 178 | symbolizers: [ 179 | [ 180 | 'r4_sy0_st0' 181 | ] 182 | ] 183 | }, { 184 | name: 'greater than', 185 | symbolizers: [ 186 | [ 187 | 'r5_sy0_st0' 188 | ] 189 | ] 190 | }, { 191 | name: 'greater than or equal to', 192 | symbolizers: [ 193 | [ 194 | 'r6_sy0_st0' 195 | ] 196 | ] 197 | }, { 198 | name: 'all', 199 | symbolizers: [ 200 | [ 201 | 'r7_sy0_st0' 202 | ] 203 | ] 204 | }, { 205 | name: 'any', 206 | symbolizers: [ 207 | [ 208 | 'r8_sy0_st0' 209 | ] 210 | ] 211 | }] 212 | } 213 | } 214 | }; 215 | 216 | export default expression_decisions; 217 | -------------------------------------------------------------------------------- /data/styles/gs_expression_math.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gs_expression_math: Style = { 5 | name: 'Expression Math', 6 | rules: [{ 7 | name: 'add', 8 | symbolizers: [{ 9 | kind: 'Mark', 10 | wellKnownName: 'circle', 11 | radius: { 12 | name: 'add', 13 | args: [13, 3, 7] 14 | } 15 | }] 16 | }, { 17 | name: 'abs', 18 | symbolizers: [{ 19 | kind: 'Mark', 20 | wellKnownName: 'circle', 21 | radius: { 22 | name: 'abs', 23 | args: [-12] 24 | } 25 | }] 26 | }, { 27 | name: 'acos', 28 | symbolizers: [{ 29 | kind: 'Mark', 30 | wellKnownName: 'circle', 31 | radius: { 32 | name: 'acos', 33 | args: [4] 34 | } 35 | }] 36 | }, { 37 | name: 'asin', 38 | symbolizers: [{ 39 | kind: 'Mark', 40 | wellKnownName: 'circle', 41 | radius: { 42 | name: 'asin', 43 | args: [4] 44 | } 45 | }] 46 | }, { 47 | name: 'atan', 48 | symbolizers: [{ 49 | kind: 'Mark', 50 | wellKnownName: 'circle', 51 | radius: { 52 | name: 'atan', 53 | args: [4] 54 | } 55 | }] 56 | }, { 57 | name: 'ceil', 58 | symbolizers: [{ 59 | kind: 'Mark', 60 | wellKnownName: 'circle', 61 | radius: { 62 | name: 'ceil', 63 | args: [3.4] 64 | } 65 | }] 66 | }, { 67 | name: 'cos', 68 | symbolizers: [{ 69 | kind: 'Mark', 70 | wellKnownName: 'circle', 71 | radius: { 72 | name: 'cos', 73 | args: [3.4] 74 | } 75 | }] 76 | }, { 77 | name: 'div', 78 | symbolizers: [{ 79 | kind: 'Mark', 80 | wellKnownName: 'circle', 81 | radius: { 82 | name: 'div', 83 | args: [100, 2] 84 | } 85 | }] 86 | }, { 87 | name: 'exp', 88 | symbolizers: [{ 89 | kind: 'Mark', 90 | wellKnownName: 'circle', 91 | radius: { 92 | name: 'exp', 93 | args: [1] 94 | } 95 | }] 96 | }, { 97 | name: 'floor', 98 | symbolizers: [{ 99 | kind: 'Mark', 100 | wellKnownName: 'circle', 101 | radius: { 102 | name: 'floor', 103 | args: [3.5] 104 | } 105 | }] 106 | }, { 107 | name: 'log', 108 | symbolizers: [{ 109 | kind: 'Mark', 110 | wellKnownName: 'circle', 111 | radius: { 112 | name: 'log', 113 | args: [4.6] 114 | } 115 | }] 116 | }, { 117 | name: 'max', 118 | symbolizers: [{ 119 | kind: 'Mark', 120 | wellKnownName: 'circle', 121 | radius: { 122 | name: 'max', 123 | args: [13, 37, 0, 8, 15] 124 | } 125 | }] 126 | }, { 127 | name: 'min', 128 | symbolizers: [{ 129 | kind: 'Mark', 130 | wellKnownName: 'circle', 131 | radius: { 132 | name: 'min', 133 | args: [13, 37, 0, 8, 15] 134 | } 135 | }] 136 | }, { 137 | name: 'modulo', 138 | symbolizers: [{ 139 | kind: 'Mark', 140 | wellKnownName: 'circle', 141 | radius: { 142 | name: 'modulo', 143 | args: [3, 2] 144 | } 145 | }] 146 | }, { 147 | name: 'mul', 148 | symbolizers: [{ 149 | kind: 'Mark', 150 | wellKnownName: 'circle', 151 | radius: { 152 | name: 'mul', 153 | args: [2, 2.5, 10] 154 | } 155 | }] 156 | }, { 157 | name: 'pi', 158 | symbolizers: [{ 159 | kind: 'Mark', 160 | wellKnownName: 'circle', 161 | radius: { 162 | name: 'pi' 163 | } 164 | }] 165 | }, { 166 | name: 'pow', 167 | symbolizers: [{ 168 | kind: 'Mark', 169 | wellKnownName: 'circle', 170 | radius: { 171 | name: 'pow', 172 | args: [2, 4] 173 | } 174 | }] 175 | }, { 176 | name: 'round', 177 | symbolizers: [{ 178 | kind: 'Mark', 179 | wellKnownName: 'circle', 180 | radius: { 181 | name: 'round', 182 | args: [13.37] 183 | } 184 | }] 185 | }, { 186 | name: 'sin', 187 | symbolizers: [{ 188 | kind: 'Mark', 189 | wellKnownName: 'circle', 190 | radius: { 191 | name: 'sin', 192 | args: [1] 193 | } 194 | }] 195 | }, { 196 | name: 'sqrt', 197 | symbolizers: [{ 198 | kind: 'Mark', 199 | wellKnownName: 'circle', 200 | radius: { 201 | name: 'sqrt', 202 | args: [1] 203 | } 204 | }] 205 | }, { 206 | name: 'sub', 207 | symbolizers: [{ 208 | kind: 'Mark', 209 | wellKnownName: 'circle', 210 | radius: { 211 | name: 'sub', 212 | args: [5, 6] 213 | } 214 | }] 215 | }, { 216 | name: 'tan', 217 | symbolizers: [{ 218 | kind: 'Mark', 219 | wellKnownName: 'circle', 220 | radius: { 221 | name: 'tan', 222 | args: [1] 223 | } 224 | }] 225 | }], 226 | metadata: { 227 | 'mapbox:ref': { 228 | sources: { 229 | testsource: { 230 | type: 'vector' 231 | } 232 | }, 233 | sourceMapping: { 234 | testsource: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] 235 | }, 236 | sourceLayerMapping: { 237 | foo: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] 238 | } 239 | } 240 | } 241 | }; 242 | 243 | export default gs_expression_math; 244 | -------------------------------------------------------------------------------- /src/Expressions.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import { beforeEach, expect, it, describe } from 'vitest'; 3 | 4 | import expression_case from '../data/mapbox/expression_case'; 5 | import expression_case_metadata from '../data/mapbox_metadata/expression_case'; 6 | import expression_get from '../data/mapbox/expression_get'; 7 | import expression_get_metadata from '../data/mapbox_metadata/expression_get'; 8 | import gs_expression_case from '../data/styles/gs_expression_case'; 9 | import gs_expression_property from '../data/styles/gs_expression_property'; 10 | import MapboxStyleParser from './MapboxStyleParser'; 11 | import expression_decisions_metadata from '../data/mapbox_metadata/expression_decisions'; 12 | import gs_expression_decisions from '../data/styles/gs_expression_decisions'; 13 | import gs_expression_math from '../data/styles/gs_expression_math'; 14 | import expression_math_metadata from '../data/mapbox_metadata/expression_math'; 15 | import gs_expression_string from '../data/styles/gs_expression_string'; 16 | import expression_string_metadata from '../data/mapbox_metadata/expression_string'; 17 | import expression_lookup_metadata from '../data/mapbox_metadata/expression_lookup'; 18 | import gs_expression_lookup from '../data/styles/gs_expression_lookup'; 19 | import gs_expression_interpolate from '../data/styles/gs_expression_interpolate'; 20 | import expression_interpolate from '../data/mapbox/expression_interpolate'; 21 | import expression_interpolate_metadata from '../data/mapbox_metadata/expression_interpolate'; 22 | 23 | describe('MapboxStyleParser can parse Expressions', () => { 24 | let styleParser: MapboxStyleParser; 25 | 26 | beforeEach(() => { 27 | styleParser = new MapboxStyleParser(); 28 | }); 29 | 30 | describe('#readStyle', () => { 31 | it('can read the "case" expression', async () => { 32 | const { output: geoStylerStyle } = await styleParser.readStyle(expression_case); 33 | expect(geoStylerStyle).toBeDefined(); 34 | expect(geoStylerStyle).toEqual(gs_expression_case); 35 | return; 36 | }); 37 | it('can read the "decision" expressions', async () => { 38 | const { output: geoStylerStyle } = await styleParser.readStyle(expression_decisions_metadata); 39 | expect(geoStylerStyle).toBeDefined(); 40 | expect(geoStylerStyle).toEqual(gs_expression_decisions); 41 | return; 42 | }); 43 | it('can read the "get" expression', async () => { 44 | const { output: geoStylerStyle } = await styleParser.readStyle(expression_get); 45 | expect(geoStylerStyle).toBeDefined(); 46 | expect(geoStylerStyle).toEqual(gs_expression_property); 47 | return; 48 | }); 49 | it('can read the "lookup" expressions', async () => { 50 | const { output: geoStylerStyle } = await styleParser.readStyle(expression_lookup_metadata); 51 | expect(geoStylerStyle).toBeDefined(); 52 | expect(geoStylerStyle).toEqual(gs_expression_lookup); 53 | return; 54 | }); 55 | it('can read the "math" expressions', async () => { 56 | const { output: geoStylerStyle } = await styleParser.readStyle(expression_math_metadata); 57 | expect(geoStylerStyle).toBeDefined(); 58 | expect(geoStylerStyle).toEqual(gs_expression_math); 59 | return; 60 | }); 61 | it('can read the "string" expressions', async () => { 62 | const { output: geoStylerStyle } = await styleParser.readStyle(expression_math_metadata); 63 | expect(geoStylerStyle).toBeDefined(); 64 | expect(geoStylerStyle).toEqual(gs_expression_math); 65 | return; 66 | }); 67 | 68 | it('can read the "interpolate" expressions', async () => { 69 | const { output: geostylerStyle } = await styleParser.readStyle(expression_interpolate); 70 | expect(geostylerStyle).toBeDefined(); 71 | expect(geostylerStyle).toEqual(gs_expression_interpolate); 72 | }); 73 | }); 74 | 75 | describe('#writeStyle', () => { 76 | it('can write the "case" expression', async () => { 77 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_case); 78 | expect(mbStyle).toBeDefined(); 79 | expect(mbStyle).toEqual(expression_case_metadata); 80 | return; 81 | }); 82 | it('can write the "decision" expressions', async () => { 83 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_decisions); 84 | expect(mbStyle).toBeDefined(); 85 | expect(mbStyle).toEqual(expression_decisions_metadata); 86 | return; 87 | }); 88 | it('can write the "lookup" expressions', async () => { 89 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_lookup); 90 | expect(mbStyle).toBeDefined(); 91 | expect(mbStyle).toEqual(expression_lookup_metadata); 92 | return; 93 | }); 94 | it('can write the "math" expressions', async () => { 95 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_math); 96 | expect(mbStyle).toBeDefined(); 97 | expect(mbStyle).toEqual(expression_math_metadata); 98 | return; 99 | }); 100 | it('can write the "property" expression', async () => { 101 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_property); 102 | expect(mbStyle).toBeDefined(); 103 | expect(mbStyle).toEqual(expression_get_metadata); 104 | return; 105 | }); 106 | it('can write the "string" expression', async () => { 107 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_string); 108 | expect(mbStyle).toBeDefined(); 109 | expect(mbStyle).toEqual(expression_string_metadata); 110 | return; 111 | }); 112 | it('can write the "interpolate" expression', async () => { 113 | const { output: mbStyle } = await styleParser.writeStyle(gs_expression_interpolate); 114 | expect(mbStyle).toBeDefined(); 115 | expect(mbStyle).toEqual(expression_interpolate_metadata); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /data/styles/gs_expression_decisions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { Style } from 'geostyler-style'; 3 | 4 | const gs_expression_decisions: Style = { 5 | name: 'Expression Decisions', 6 | rules: [{ 7 | name: 'negation', 8 | symbolizers: [{ 9 | kind: 'Mark', 10 | wellKnownName: 'circle', 11 | color: { 12 | name: 'case', 13 | args: [ 14 | '#000000', 15 | { 16 | case: { 17 | name: 'not', 18 | args: [{ 19 | name: 'equalTo', 20 | args: [{ 21 | name: 'property', 22 | args: ['mag'] 23 | }, 1] 24 | }] 25 | }, 26 | value: '#FFFFFF' 27 | } 28 | ] 29 | } 30 | }] 31 | }, { 32 | name: 'not equal to', 33 | symbolizers: [{ 34 | kind: 'Mark', 35 | wellKnownName: 'circle', 36 | color: { 37 | name: 'case', 38 | args: [ 39 | '#000000', 40 | { 41 | case: { 42 | name: 'notEqualTo', 43 | args: [{ 44 | name: 'property', 45 | args: ['mag'] 46 | }, 1] 47 | }, 48 | value: '#FFFFFF' 49 | } 50 | ] 51 | } 52 | }] 53 | }, { 54 | name: 'less than', 55 | symbolizers: [{ 56 | kind: 'Mark', 57 | wellKnownName: 'circle', 58 | color: { 59 | name: 'case', 60 | args: [ 61 | '#000000', 62 | { 63 | case: { 64 | name: 'lessThan', 65 | args: [{ 66 | name: 'property', 67 | args: ['mag'] 68 | }, 1] 69 | }, 70 | value: '#FFFFFF' 71 | } 72 | ] 73 | } 74 | }] 75 | }, { 76 | name: 'less than or equal to', 77 | symbolizers: [{ 78 | kind: 'Mark', 79 | wellKnownName: 'circle', 80 | color: { 81 | name: 'case', 82 | args: [ 83 | '#000000', 84 | { 85 | case: { 86 | name: 'lessThanOrEqualTo', 87 | args: [{ 88 | name: 'property', 89 | args: ['mag'] 90 | }, 1] 91 | }, 92 | value: '#FFFFFF' 93 | } 94 | ] 95 | } 96 | }] 97 | }, { 98 | name: 'equal to', 99 | symbolizers: [{ 100 | kind: 'Mark', 101 | wellKnownName: 'circle', 102 | color: { 103 | name: 'case', 104 | args: [ 105 | '#000000', 106 | { 107 | case: { 108 | name: 'equalTo', 109 | args: [{ 110 | name: 'property', 111 | args: ['mag'] 112 | }, 1] 113 | }, 114 | value: '#FFFFFF' 115 | } 116 | ] 117 | } 118 | }] 119 | }, { 120 | name: 'greater than', 121 | symbolizers: [{ 122 | kind: 'Mark', 123 | wellKnownName: 'circle', 124 | color: { 125 | name: 'case', 126 | args: [ 127 | '#000000', 128 | { 129 | case: { 130 | name: 'greaterThan', 131 | args: [{ 132 | name: 'property', 133 | args: ['mag'] 134 | }, 1] 135 | }, 136 | value: '#FFFFFF' 137 | } 138 | ] 139 | } 140 | }] 141 | }, { 142 | name: 'greater than or equal to', 143 | symbolizers: [{ 144 | kind: 'Mark', 145 | wellKnownName: 'circle', 146 | color: { 147 | name: 'case', 148 | args: [ 149 | '#000000', 150 | { 151 | case: { 152 | name: 'greaterThanOrEqualTo', 153 | args: [{ 154 | name: 'property', 155 | args: ['mag'] 156 | }, 1] 157 | }, 158 | value: '#FFFFFF' 159 | } 160 | ] 161 | } 162 | }] 163 | }, { 164 | name: 'all', 165 | symbolizers: [{ 166 | kind: 'Mark', 167 | wellKnownName: 'circle', 168 | color: { 169 | name: 'case', 170 | args: [ 171 | '#000000', 172 | { 173 | case: { 174 | name: 'all', 175 | args: [ 176 | { 177 | name: 'greaterThanOrEqualTo', 178 | args: [{ 179 | name: 'property', 180 | args: ['mag'] 181 | }, 1] 182 | }, 183 | { 184 | name: 'lessThanOrEqualTo', 185 | args: [{ 186 | name: 'property', 187 | args: ['mag'] 188 | }, 2] 189 | } 190 | ] 191 | }, 192 | value: '#FFFFFF' 193 | } 194 | ] 195 | } 196 | }] 197 | }, { 198 | name: 'any', 199 | symbolizers: [{ 200 | kind: 'Mark', 201 | wellKnownName: 'circle', 202 | color: { 203 | name: 'case', 204 | args: [ 205 | '#000000', 206 | { 207 | case: { 208 | name: 'any', 209 | args: [ 210 | { 211 | name: 'equalTo', 212 | args: [{ 213 | name: 'property', 214 | args: ['mag'] 215 | }, 'a'] 216 | }, 217 | { 218 | name: 'equalTo', 219 | args: [{ 220 | name: 'property', 221 | args: ['mag'] 222 | }, 'b'] 223 | } 224 | ] 225 | }, 226 | value: '#FFFFFF' 227 | } 228 | ] 229 | } 230 | }] 231 | }], 232 | metadata: { 233 | 'mapbox:ref': { 234 | sources: { 235 | testsource: { 236 | type: 'vector' 237 | } 238 | }, 239 | sourceMapping: { 240 | testsource: [0, 1, 2, 3, 4, 5, 6, 7, 8] 241 | }, 242 | sourceLayerMapping: { 243 | foo: [0, 1, 2, 3, 4, 5, 6, 7, 8] 244 | } 245 | } 246 | } 247 | }; 248 | 249 | export default gs_expression_decisions; 250 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [6.2.0](https://github.com/geostyler/geostyler-mapbox-parser/compare/v6.1.1...v6.2.0) (2025-12-16) 2 | 3 | ### Features 4 | 5 | * add option to replace markers with solid color ([7fe7467](https://github.com/geostyler/geostyler-mapbox-parser/commit/7fe7467ee015da5298d01980bd9fffce76dd95f0)) 6 | * add prepare script to allow installing a git reference ([10689b8](https://github.com/geostyler/geostyler-mapbox-parser/commit/10689b8a1ef2290709af96dadfe6a5b4bb24941a)) 7 | * update dependencies and switch to bun ([70e8ade](https://github.com/geostyler/geostyler-mapbox-parser/commit/70e8adea3931b0914598dc6be6fc5bac989ad9f1)) 8 | 9 | ### Bug Fixes 10 | 11 | * add imported indirect dependency to devDependencies ([970b788](https://github.com/geostyler/geostyler-mapbox-parser/commit/970b7889eb4ebd83e5021fcbab49b5a4da1a7d74)) 12 | * add test for graphicFill fallback ([1fbd8a0](https://github.com/geostyler/geostyler-mapbox-parser/commit/1fbd8a066306ebf244c669a9e9a4c0ccc13fd4e1)) 13 | * fix workflow ([046c2ac](https://github.com/geostyler/geostyler-mapbox-parser/commit/046c2ac14d9ec2669165b0094db566668c26f53e)) 14 | * fix workflow ([41b35f4](https://github.com/geostyler/geostyler-mapbox-parser/commit/41b35f459c2cfe6e1ae0c17f8e4537437cb5184c)) 15 | * fix workflow ([14b0226](https://github.com/geostyler/geostyler-mapbox-parser/commit/14b02268fadc0ca6ab8e8347de495ae0fe13c57c)) 16 | * fix workflow ([b0b344e](https://github.com/geostyler/geostyler-mapbox-parser/commit/b0b344ec793139eee37a9af2478a5a885a29110e)) 17 | * fix workflow ([f76a1f1](https://github.com/geostyler/geostyler-mapbox-parser/commit/f76a1f1d314b7a4b4ee3bad2ef069cef48017ea7)) 18 | * fix workflow ([f8c0a26](https://github.com/geostyler/geostyler-mapbox-parser/commit/f8c0a268be00fd748bddccb8717c1265d05bba37)) 19 | * fix workflow ([85aa519](https://github.com/geostyler/geostyler-mapbox-parser/commit/85aa5194ba668f4c4067d502cb0412e3e40daf91)) 20 | * fix workflow ([2073066](https://github.com/geostyler/geostyler-mapbox-parser/commit/2073066eadd8086cb866f273780b794d81443b1c)) 21 | * fix workflow ([719f72c](https://github.com/geostyler/geostyler-mapbox-parser/commit/719f72cdf0166fe606c2fcaf8e4a1fb0bb81b812)) 22 | * fix workflow ([1ffe937](https://github.com/geostyler/geostyler-mapbox-parser/commit/1ffe937e2f1453fd46883b04d355651c82b5811f)) 23 | * update dependency, fix workflow ([c5f64be](https://github.com/geostyler/geostyler-mapbox-parser/commit/c5f64be1d1c85492b89e780f8068bc6e66a2b980)) 24 | 25 | ## [6.1.1](https://github.com/geostyler/geostyler-mapbox-parser/compare/v6.1.0...v6.1.1) (2025-03-12) 26 | 27 | ### Bug Fixes 28 | 29 | * **deps:** update dependency geostyler-style to v10 ([7661add](https://github.com/geostyler/geostyler-mapbox-parser/commit/7661add5aa0c8d2c44f6cc28142aaf90e6645ae7)) 30 | 31 | ## [6.1.0](https://github.com/geostyler/geostyler-mapbox-parser/compare/v6.0.1...v6.1.0) (2024-12-02) 32 | 33 | 34 | ### Features 35 | 36 | * add interpolate function ([#325](https://github.com/geostyler/geostyler-mapbox-parser/issues/325)) ([6567d8b](https://github.com/geostyler/geostyler-mapbox-parser/commit/6567d8b14c0158f96856de8786081d4fd0f554ff)) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **deps:** update dependency @types/mapbox-gl to v2.7.21 ([d7cf5ea](https://github.com/geostyler/geostyler-mapbox-parser/commit/d7cf5ea2d80a4baae9b5990f9b90ac7c5fa5b330)) 42 | * preserve outline dash array ([77edffb](https://github.com/geostyler/geostyler-mapbox-parser/commit/77edffbf967d31c668e6b4a2e8c3a8d90b9517a4)) 43 | 44 | ## [6.0.1](https://github.com/geostyler/geostyler-mapbox-parser/compare/v6.0.0...v6.0.1) (2024-11-11) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * preserve visibility in line symbolizer during cloning ([dfda998](https://github.com/geostyler/geostyler-mapbox-parser/commit/dfda9982199eb1769d3b1a103d26940517949874)) 50 | 51 | ## [6.0.0](https://github.com/geostyler/geostyler-mapbox-parser/compare/v5.0.1...v6.0.0) (2024-06-25) 52 | 53 | 54 | ### ⚠ BREAKING CHANGES 55 | 56 | * Switches to esm build and to vite. Users possibly 57 | need to adapt their imports and their bundler. 58 | 59 | ### Features 60 | 61 | * prepare next release ([f110eec](https://github.com/geostyler/geostyler-mapbox-parser/commit/f110eecbdedd3830a03be0b31cb13615fbc03027)) 62 | * read rgba color values into hex ([#323](https://github.com/geostyler/geostyler-mapbox-parser/issues/323)) ([e47192d](https://github.com/geostyler/geostyler-mapbox-parser/commit/e47192d7a62b9e70f9e1d2fd36e8f74f1abe6dcd)) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * update dependencies ([5926b1b](https://github.com/geostyler/geostyler-mapbox-parser/commit/5926b1b087ca05ac3825b9fb262756f27510112f)) 68 | * update resolutions to levels 0-24 ([#320](https://github.com/geostyler/geostyler-mapbox-parser/issues/320)) ([b02d9a5](https://github.com/geostyler/geostyler-mapbox-parser/commit/b02d9a5bbafba4d544f935c3ac40a785cbb7ecec)) 69 | 70 | ## [6.0.0-next.2](https://github.com/geostyler/geostyler-mapbox-parser/compare/v6.0.0-next.1...v6.0.0-next.2) (2024-06-25) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * update dependencies ([5926b1b](https://github.com/geostyler/geostyler-mapbox-parser/commit/5926b1b087ca05ac3825b9fb262756f27510112f)) 76 | 77 | ## [6.0.0-next.1](https://github.com/geostyler/geostyler-mapbox-parser/compare/v5.0.1...v6.0.0-next.1) (2024-06-21) 78 | 79 | 80 | ### ⚠ BREAKING CHANGES 81 | 82 | * Switches to esm build and to vite. Users possibly 83 | need to adapt their imports and their bundler. 84 | 85 | ### Features 86 | 87 | * prepare next release ([f110eec](https://github.com/geostyler/geostyler-mapbox-parser/commit/f110eecbdedd3830a03be0b31cb13615fbc03027)) 88 | * read rgba color values into hex ([#323](https://github.com/geostyler/geostyler-mapbox-parser/issues/323)) ([e47192d](https://github.com/geostyler/geostyler-mapbox-parser/commit/e47192d7a62b9e70f9e1d2fd36e8f74f1abe6dcd)) 89 | 90 | 91 | ### Bug Fixes 92 | 93 | * update resolutions to levels 0-24 ([#320](https://github.com/geostyler/geostyler-mapbox-parser/issues/320)) ([b02d9a5](https://github.com/geostyler/geostyler-mapbox-parser/commit/b02d9a5bbafba4d544f935c3ac40a785cbb7ecec)) 94 | 95 | ## [5.0.1](https://github.com/geostyler/geostyler-mapbox-parser/compare/v5.0.0...v5.0.1) (2023-12-05) 96 | 97 | 98 | ### Bug Fixes 99 | 100 | * allow scale to be a GeoStylerFunction ([73d2271](https://github.com/geostyler/geostyler-mapbox-parser/commit/73d2271e536b14604ca222aaa9af6972014930aa)) 101 | * treat ison-size as scale ([ebc9434](https://github.com/geostyler/geostyler-mapbox-parser/commit/ebc9434443ffa4ead4d0bf0296b3422cc73aa2c4)) 102 | * use width and multiply directly ([113ef5e](https://github.com/geostyler/geostyler-mapbox-parser/commit/113ef5e8c051e4433971fd3034ff102922f74adf)) 103 | 104 | ## [5.0.0](https://github.com/geostyler/geostyler-mapbox-parser/compare/v4.0.0...v5.0.0) (2023-12-04) 105 | 106 | 107 | ### ⚠ BREAKING CHANGES 108 | 109 | * use geostyler-style Sprite instead of string syntax 110 | * This changes the typing of the MbStyle by making 111 | the property 'source' required. Through this, we actually follow 112 | the MapBox Style Specification. MbStyles that previously omitted the 113 | 'source' property, will now have to add it. 114 | 115 | ### Features 116 | 117 | * add sprite support ([9d883be](https://github.com/geostyler/geostyler-mapbox-parser/commit/9d883be90463480fc1cd71e3bce48aaf43d88405)) 118 | * add spriteCache ([9043e10](https://github.com/geostyler/geostyler-mapbox-parser/commit/9043e10a93a0394cdda2e4fcb575abefbc3e9c1e)) 119 | * keep track of mapbox sources and source layers in layers ([a8f5d7c](https://github.com/geostyler/geostyler-mapbox-parser/commit/a8f5d7c787a78dc9d59d91ac14e52ef8686c4822)) 120 | * parse placement of TextSymbolizer ([#292](https://github.com/geostyler/geostyler-mapbox-parser/issues/292)) ([9acb43f](https://github.com/geostyler/geostyler-mapbox-parser/commit/9acb43f377bca6c376155e0b6b0b9d09d9c4cc4c)) 121 | 122 | 123 | ### Bug Fixes 124 | 125 | * address comments and add docs ([eca7607](https://github.com/geostyler/geostyler-mapbox-parser/commit/eca760769e3e7c4a1b6869de7050f99c0ba97fab)) 126 | * **lint:** remove test util ([9a01114](https://github.com/geostyler/geostyler-mapbox-parser/commit/9a01114f3be74aae80c46ba622d3dfecde73851b)) 127 | * merge icontext symbol from separate symbolizers when needed ([a02a565](https://github.com/geostyler/geostyler-mapbox-parser/commit/a02a5650311723cb2d741c5a890a3b3c6b59e23e)) 128 | * mutate only clones of arguments ([36a76d0](https://github.com/geostyler/geostyler-mapbox-parser/commit/36a76d0af0560ef8fce43e63144f06d61565cf9f)) 129 | * typos ([2de63cc](https://github.com/geostyler/geostyler-mapbox-parser/commit/2de63cc64d3b7c7ad81efc6308c5fa372d6f5196)) 130 | -------------------------------------------------------------------------------- /data/mapbox_metadata/expression_math.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import { MbStyle } from '../../src/MapboxStyleParser'; 3 | 4 | const expression_math: MbStyle = { 5 | version: 8, 6 | name: 'Expression Math', 7 | sources: { 8 | testsource: { 9 | type: 'vector' 10 | } 11 | }, 12 | layers: [ 13 | { 14 | id: 'r0_sy0_st0', 15 | source: 'testsource', 16 | 'source-layer': 'foo', 17 | type: 'circle', 18 | paint: { 19 | 'circle-radius': ['+', 13, 3, 7] 20 | } 21 | }, 22 | { 23 | id: 'r1_sy0_st0', 24 | source: 'testsource', 25 | 'source-layer': 'foo', 26 | type: 'circle', 27 | paint: { 28 | 'circle-radius': ['abs', -12] 29 | } 30 | }, 31 | { 32 | id: 'r2_sy0_st0', 33 | source: 'testsource', 34 | 'source-layer': 'foo', 35 | type: 'circle', 36 | paint: { 37 | 'circle-radius': ['acos', 4] 38 | } 39 | }, 40 | { 41 | id: 'r3_sy0_st0', 42 | source: 'testsource', 43 | 'source-layer': 'foo', 44 | type: 'circle', 45 | paint: { 46 | 'circle-radius': ['asin', 4] 47 | } 48 | }, 49 | { 50 | id: 'r4_sy0_st0', 51 | source: 'testsource', 52 | 'source-layer': 'foo', 53 | type: 'circle', 54 | paint: { 55 | 'circle-radius': ['atan', 4] 56 | } 57 | }, 58 | { 59 | id: 'r5_sy0_st0', 60 | source: 'testsource', 61 | 'source-layer': 'foo', 62 | type: 'circle', 63 | paint: { 64 | 'circle-radius': ['ceil', 3.4] 65 | } 66 | }, 67 | { 68 | id: 'r6_sy0_st0', 69 | source: 'testsource', 70 | 'source-layer': 'foo', 71 | type: 'circle', 72 | paint: { 73 | 'circle-radius': ['cos', 3.4] 74 | } 75 | }, 76 | { 77 | id: 'r7_sy0_st0', 78 | source: 'testsource', 79 | 'source-layer': 'foo', 80 | type: 'circle', 81 | paint: { 82 | 'circle-radius': ['/', 100, 2] 83 | } 84 | }, 85 | { 86 | id: 'r8_sy0_st0', 87 | source: 'testsource', 88 | 'source-layer': 'foo', 89 | type: 'circle', 90 | paint: { 91 | 'circle-radius': ['e'] 92 | } 93 | }, 94 | { 95 | id: 'r9_sy0_st0', 96 | source: 'testsource', 97 | 'source-layer': 'foo', 98 | type: 'circle', 99 | paint: { 100 | 'circle-radius': ['floor', 3.5] 101 | } 102 | }, 103 | { 104 | id: 'r10_sy0_st0', 105 | source: 'testsource', 106 | 'source-layer': 'foo', 107 | type: 'circle', 108 | paint: { 109 | 'circle-radius': ['ln', 4.6] 110 | } 111 | }, 112 | { 113 | id: 'r11_sy0_st0', 114 | source: 'testsource', 115 | 'source-layer': 'foo', 116 | type: 'circle', 117 | paint: { 118 | 'circle-radius': ['max', 13, 37, 0, 8, 15] 119 | } 120 | }, 121 | { 122 | id: 'r12_sy0_st0', 123 | source: 'testsource', 124 | 'source-layer': 'foo', 125 | type: 'circle', 126 | paint: { 127 | 'circle-radius': ['min', 13, 37, 0, 8, 15] 128 | } 129 | }, 130 | { 131 | id: 'r13_sy0_st0', 132 | source: 'testsource', 133 | 'source-layer': 'foo', 134 | type: 'circle', 135 | paint: { 136 | 'circle-radius': ['%', 3, 2] 137 | } 138 | }, 139 | { 140 | id: 'r14_sy0_st0', 141 | source: 'testsource', 142 | 'source-layer': 'foo', 143 | type: 'circle', 144 | paint: { 145 | 'circle-radius': ['*', 2, 2.5, 10] 146 | } 147 | }, 148 | { 149 | id: 'r15_sy0_st0', 150 | source: 'testsource', 151 | 'source-layer': 'foo', 152 | type: 'circle', 153 | paint: { 154 | 'circle-radius': ['pi'] 155 | } 156 | }, 157 | { 158 | id: 'r16_sy0_st0', 159 | source: 'testsource', 160 | 'source-layer': 'foo', 161 | type: 'circle', 162 | paint: { 163 | 'circle-radius': ['^', 2, 4] 164 | } 165 | }, 166 | { 167 | id: 'r17_sy0_st0', 168 | source: 'testsource', 169 | 'source-layer': 'foo', 170 | type: 'circle', 171 | paint: { 172 | 'circle-radius': ['round', 13.37] 173 | } 174 | }, 175 | { 176 | id: 'r18_sy0_st0', 177 | source: 'testsource', 178 | 'source-layer': 'foo', 179 | type: 'circle', 180 | paint: { 181 | 'circle-radius': ['sin', 1] 182 | } 183 | }, 184 | { 185 | id: 'r19_sy0_st0', 186 | source: 'testsource', 187 | 'source-layer': 'foo', 188 | type: 'circle', 189 | paint: { 190 | 'circle-radius': ['sqrt', 1] 191 | } 192 | }, 193 | { 194 | id: 'r20_sy0_st0', 195 | source: 'testsource', 196 | 'source-layer': 'foo', 197 | type: 'circle', 198 | paint: { 199 | 'circle-radius': ['-', 5, 6] 200 | } 201 | }, 202 | { 203 | id: 'r21_sy0_st0', 204 | source: 'testsource', 205 | 'source-layer': 'foo', 206 | type: 'circle', 207 | paint: { 208 | 'circle-radius': ['tan', 1] 209 | } 210 | } 211 | ], 212 | metadata: { 213 | 'geostyler:ref': { 214 | rules: [{ 215 | name: 'add', 216 | symbolizers: [ 217 | [ 218 | 'r0_sy0_st0' 219 | ] 220 | ] 221 | }, { 222 | name: 'abs', 223 | symbolizers: [ 224 | [ 225 | 'r1_sy0_st0' 226 | ] 227 | ] 228 | }, { 229 | name: 'acos', 230 | symbolizers: [ 231 | [ 232 | 'r2_sy0_st0' 233 | ] 234 | ] 235 | }, { 236 | name: 'asin', 237 | symbolizers: [ 238 | [ 239 | 'r3_sy0_st0' 240 | ] 241 | ] 242 | }, { 243 | name: 'atan', 244 | symbolizers: [ 245 | [ 246 | 'r4_sy0_st0' 247 | ] 248 | ] 249 | }, { 250 | name: 'ceil', 251 | symbolizers: [ 252 | [ 253 | 'r5_sy0_st0' 254 | ] 255 | ] 256 | }, { 257 | name: 'cos', 258 | symbolizers: [ 259 | [ 260 | 'r6_sy0_st0' 261 | ] 262 | ] 263 | }, { 264 | name: 'div', 265 | symbolizers: [ 266 | [ 267 | 'r7_sy0_st0' 268 | ] 269 | ] 270 | }, { 271 | name: 'exp', 272 | symbolizers: [ 273 | [ 274 | 'r8_sy0_st0' 275 | ] 276 | ] 277 | }, { 278 | name: 'floor', 279 | symbolizers: [ 280 | [ 281 | 'r9_sy0_st0' 282 | ] 283 | ] 284 | }, { 285 | name: 'log', 286 | symbolizers: [ 287 | [ 288 | 'r10_sy0_st0' 289 | ] 290 | ] 291 | }, { 292 | name: 'max', 293 | symbolizers: [ 294 | [ 295 | 'r11_sy0_st0' 296 | ] 297 | ] 298 | }, { 299 | name: 'min', 300 | symbolizers: [ 301 | [ 302 | 'r12_sy0_st0' 303 | ] 304 | ] 305 | }, { 306 | name: 'modulo', 307 | symbolizers: [ 308 | [ 309 | 'r13_sy0_st0' 310 | ] 311 | ] 312 | }, { 313 | name: 'mul', 314 | symbolizers: [ 315 | [ 316 | 'r14_sy0_st0' 317 | ] 318 | ] 319 | }, { 320 | name: 'pi', 321 | symbolizers: [ 322 | [ 323 | 'r15_sy0_st0' 324 | ] 325 | ] 326 | }, { 327 | name: 'pow', 328 | symbolizers: [ 329 | [ 330 | 'r16_sy0_st0' 331 | ] 332 | ] 333 | }, { 334 | name: 'round', 335 | symbolizers: [ 336 | [ 337 | 'r17_sy0_st0' 338 | ] 339 | ] 340 | }, { 341 | name: 'sin', 342 | symbolizers: [ 343 | [ 344 | 'r18_sy0_st0' 345 | ] 346 | ] 347 | }, { 348 | name: 'sqrt', 349 | symbolizers: [ 350 | [ 351 | 'r19_sy0_st0' 352 | ] 353 | ] 354 | }, { 355 | name: 'sub', 356 | symbolizers: [ 357 | [ 358 | 'r20_sy0_st0' 359 | ] 360 | ] 361 | }, { 362 | name: 'tan', 363 | symbolizers: [ 364 | [ 365 | 'r21_sy0_st0' 366 | ] 367 | ] 368 | }] 369 | } 370 | } 371 | }; 372 | 373 | export default expression_math; 374 | -------------------------------------------------------------------------------- /src/Util/MapboxStyleUtil.ts: -------------------------------------------------------------------------------- 1 | import { Sprite, Symbolizer, TextSymbolizer } from 'geostyler-style'; 2 | import { MapboxRef } from '../MapboxStyleParser'; 3 | 4 | class MapboxStyleUtil { 5 | 6 | public static getResolutions(): number[] { 7 | const resolutions = []; 8 | let res = 78271.51696402048; 9 | // adding resolution for arbitrary zoom level 0 10 | // This simplyfies working with zoom levels but might lead to unexpected 11 | // behaviour. 12 | resolutions.push(res * 2); 13 | for (res; resolutions.length <= 24; res /= 2) { 14 | resolutions.push(res); 15 | } 16 | return resolutions; 17 | } 18 | // credits to 19 | // https://github.com/terrestris/ol-util/blob/de1b580c63454c8110806a3d73a5f6e972b2f2b0/src/MapUtil/MapUtil.js#L104 20 | public static getScaleForResolution(resolution: number): number { 21 | var dpi = 25.4 / 0.28; 22 | var mpu = 1; 23 | var inchesPerMeter = 39.37008; 24 | 25 | return resolution * mpu * inchesPerMeter * dpi; 26 | } 27 | 28 | // credits to 29 | // https://github.com/openlayers/ol-mapbox-style/blob/e632c935e7e34bd27079b7fc234202a9ac3b73ee/util.js 30 | public static getZoomForResolution(resolution: number): number { 31 | let i = 0; 32 | const resolutions = MapboxStyleUtil.getResolutions(); 33 | const ii = resolutions.length; 34 | for (; i < ii; ++i) { 35 | const candidate = resolutions[i]; 36 | if (candidate < resolution && i + 1 < ii) { 37 | const zoomFactor = resolutions[i] / resolutions[i + 1]; 38 | return i + Math.log(resolutions[i] / resolution) / Math.log(zoomFactor); 39 | } 40 | } 41 | return ii - 1; 42 | } 43 | 44 | /** 45 | * Calculates the appropriate map resolution for a given scale in the given 46 | * units. 47 | * 48 | * See: https://gis.stackexchange.com/questions/158435/ 49 | * how-to-get-current-scale-in-openlayers-3 50 | * 51 | * @method 52 | * @param {number} scale The input scale to calculate the appropriate 53 | * resolution for. 54 | * @return {number} The calculated resolution. 55 | */ 56 | static getResolutionForScale(scale: number): number { 57 | let dpi = 25.4 / 0.28; 58 | let mpu = 1; 59 | let inchesPerMeter = 39.37008; 60 | 61 | return scale / (mpu * inchesPerMeter * dpi); 62 | } 63 | 64 | public static zoomToScale(zoom: number): number { 65 | const resolutions = MapboxStyleUtil.getResolutions(); 66 | // if zoom is integer 67 | if (zoom >= resolutions.length) { 68 | throw new Error('Cannot parse scaleDenominator. ZoomLevel does not exist.'); 69 | } 70 | let resolution: number; 71 | if (Number.isInteger(zoom)) { 72 | resolution = resolutions[zoom]; 73 | } else { 74 | // interpolate values 75 | const pre = Math.floor(zoom); 76 | const preVal = resolutions[pre]; 77 | // after carefully rearranging 78 | // zoom = i + Math.log(resolutions[i] / resolution) / Math.log(zoomFactor) 79 | // with the zoomFactor being 2 I've arrived at this formula to properly 80 | // calculate the resolution: 81 | resolution = Math.pow(2, pre) * preVal / Math.pow(2, zoom); 82 | // this still gives some smallish rounding errors, but at the 8th digit after 83 | // the dot this is ok 84 | } 85 | return this.getScaleForResolution(resolution); 86 | } 87 | 88 | /** 89 | * Checks if all keys of an object are undefined. 90 | * Returns true if so. 91 | * 92 | * @param obj The object to be checked 93 | */ 94 | public static allUndefined(obj: any): boolean { 95 | if (!obj) { 96 | return true; 97 | } 98 | const keys = Object.keys(obj); 99 | return !keys.some((k: string) => { 100 | return typeof obj[k] !== 'undefined'; 101 | }); 102 | } 103 | 104 | /** 105 | * Checks if all keys of a Symbolizer are undefined except 'kind'. 106 | * 107 | * @param symbolizer A GeoStylerStyle Symbolizer 108 | */ 109 | // TODO: TextSymbolizer can be removed once it is fixed in the geostyler-style 110 | public static symbolizerAllUndefined(symbolizer: Symbolizer | TextSymbolizer): boolean { 111 | return !Object.keys(symbolizer) 112 | .filter(val => val !== 'kind' && val !== 'visibility') 113 | .some((val: keyof Symbolizer) => typeof symbolizer[val] !== 'undefined'); 114 | } 115 | 116 | /** 117 | * Replaces the mapbox api placeholder with its actual url. 118 | * 119 | * @param url URL 120 | */ 121 | public static getUrlForMbPlaceholder(url: string): string { 122 | const mbPlaceholder = 'mapbox://'; 123 | const mbUrl = 'https://api.mapbox.com/'; 124 | if (url && url.startsWith(mbPlaceholder)) { 125 | return url.replace(mbPlaceholder, mbUrl); 126 | } 127 | return url; 128 | } 129 | 130 | /** 131 | * Replaces the actual mapbox url with its api placeholder. 132 | * 133 | * @param url URL 134 | */ 135 | public static getMbPlaceholderForUrl(url: string): string { 136 | const mbPlaceholder = 'mapbox://'; 137 | const mbUrl = 'https://api.mapbox.com/'; 138 | if (url && url.startsWith(mbUrl)) { 139 | return url.replace(mbUrl, mbPlaceholder); 140 | } 141 | return url; 142 | } 143 | 144 | /** 145 | * Resolves a mapbox text-field placeholder string to a geostyler-style 146 | * placeholder string. I.e. replaces {varname} with {{varname}}. 147 | * 148 | * @param template Template string that should be resolved 149 | */ 150 | public static resolveMbTextPlaceholder(template: string): string { 151 | // prefix indicating that a template is being used 152 | const prefix: string = '\\{'; 153 | // suffix indicating that a template is being used 154 | const suffix: string = '\\}'; 155 | 156 | let regExp: RegExp = new RegExp(prefix + '.*?' + suffix, 'g'); 157 | const gsLabel = template.replace(regExp, (match: string) => { 158 | return `{${match}}`; 159 | }); 160 | return gsLabel; 161 | } 162 | 163 | public static getSpriteName(sprite: Sprite, metadata: MapboxRef): string { 164 | if (!metadata?.sprite || !sprite) { 165 | throw new Error('Cannot retrieve sprite name. Sprite or metadata missing.'); 166 | } 167 | const name = Object.keys(metadata.sprite) 168 | .find(key => { 169 | const value = metadata.sprite![key]; 170 | return value.position[0] === sprite.position[0] && 171 | value.position[1] === sprite.position[1] && 172 | value.size[0] === sprite.size[0] && 173 | value.size[1] === sprite.size[1]; 174 | }); 175 | if (!name) { 176 | throw new Error('Cannot retrieve sprite name. No matching sprite in metadata.'); 177 | } 178 | return name || ''; 179 | } 180 | 181 | /** 182 | * Splits a RGBA encoded color into its color values. 183 | * 184 | * @param {string} rgbaColor RGB(A) encoded color 185 | * @return {number[]} Numeric color values as array 186 | */ 187 | public static splitRgbaColor(rgbaColor: string): number[] { 188 | const colorsOnly = rgbaColor.substring(rgbaColor.indexOf('(') + 1, rgbaColor.lastIndexOf(')')).split(/,\s*/); 189 | const red = parseInt(colorsOnly[0], 10); 190 | const green = parseInt(colorsOnly[1], 10); 191 | const blue = parseInt(colorsOnly[2], 10); 192 | const opacity = parseFloat(colorsOnly[3]); 193 | 194 | return [red, green, blue, opacity]; 195 | } 196 | 197 | /** 198 | * Returns the hex code for a given RGB(A) array. 199 | * 200 | * @param colorArr RGB(A) array. e.g. [255,0,0] 201 | * @return {string} The HEX color representation of the given color 202 | */ 203 | public static getHexCodeFromRgbArray(colorArr: number[]): string { 204 | return '#' + colorArr.map((x, idx) => { 205 | const hex = x.toString(16); 206 | // skip opacity if passed as fourth entry 207 | if (idx < 3) { 208 | return hex.length === 1 ? '0' + hex : hex; 209 | } 210 | return ''; 211 | }).join(''); 212 | } 213 | 214 | /** 215 | * Transforms a RGB(A) or named color value to a HEX encoded notation. 216 | * If a HEX color is provided it will be returned untransformed. 217 | * 218 | * @param {string} inColor The color to transform 219 | * @return {string | undefined} The HEX color representation of the given color 220 | */ 221 | public static getHexColor(inColor: string): string | undefined { 222 | // if passing in a hex code we just return it 223 | if (inColor.startsWith('#')) { 224 | return inColor; 225 | } else if (inColor.startsWith('rgb')) { 226 | const colorArr = this.splitRgbaColor(inColor); 227 | return this.getHexCodeFromRgbArray(colorArr); 228 | } else { 229 | return; 230 | } 231 | } 232 | 233 | } 234 | 235 | export default MapboxStyleUtil; 236 | -------------------------------------------------------------------------------- /src/Expressions.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Expression, 3 | Fcase, 4 | Finterpolate, 5 | Expression as GeoStylerExpression, 6 | GeoStylerFunction, 7 | PropertyType, 8 | isGeoStylerFunction 9 | } from 'geostyler-style'; 10 | import { invert } from 'lodash'; 11 | import { 12 | ExpressionName, 13 | Expression as MapboxExpression, 14 | StyleFunction 15 | } from 'mapbox-gl'; 16 | 17 | import MapboxStyleUtil from './Util/MapboxStyleUtil'; 18 | 19 | const expressionNames: ExpressionName[] = ['array', 20 | 'boolean', 21 | 'collator', 22 | 'format', 23 | 'literal', 24 | 'number', 25 | 'number-format', 26 | 'object', 27 | 'string', 28 | 'image', 29 | 'to-boolean', 30 | 'to-color', 31 | 'to-number', 32 | 'to-string', 33 | 'typeof', 34 | 'feature-state', 35 | 'geometry-type', 36 | 'id', 37 | 'line-progress', 38 | 'properties', 39 | 'at', 40 | 'get', 41 | 'has', 42 | 'in', 43 | 'index-of', 44 | 'length', 45 | 'slice', 46 | '!', 47 | '!=', 48 | '<', 49 | '<=', 50 | '==', 51 | '>', 52 | '>=', 53 | 'all', 54 | 'any', 55 | 'case', 56 | 'match', 57 | 'coalesce', 58 | 'within', 59 | 'interpolate', 60 | 'interpolate-hcl', 61 | 'interpolate-lab', 62 | 'step', 63 | 'let', 64 | 'var', 65 | 'concat', 66 | 'downcase', 67 | 'is-supported-script', 68 | 'resolved-locale', 69 | 'upcase', 70 | 'rgb', 71 | 'rgba', 72 | 'to-rgba', 73 | '-', 74 | '*', 75 | '/', 76 | '%', 77 | '^', 78 | '+', 79 | 'abs', 80 | 'acos', 81 | 'asin', 82 | 'atan', 83 | 'ceil', 84 | 'cos', 85 | 'e', 86 | 'floor', 87 | 'ln', 88 | 'ln2', 89 | 'log10', 90 | 'log2', 91 | 'max', 92 | 'min', 93 | 'pi', 94 | 'round', 95 | 'sin', 96 | 'sqrt', 97 | 'tan', 98 | 'zoom', 99 | 'heatmap-density']; 100 | 101 | const functionNameMap: Record = { 102 | // ---- string ---- 103 | numberFormat: null, 104 | // numberFormat: 'number-format', // TODO: this could be done in theory but gs and mb use different format approaches 105 | strAbbreviate: null, 106 | strCapitalize: null, 107 | strConcat: 'concat', 108 | strDefaultIfBlank: null, 109 | strEndsWith: null, 110 | strEqualsIgnoreCase: null, 111 | strIndexOf: null, 112 | strLastIndexOf: null, 113 | strLength: 'length', 114 | strMatches: null, 115 | strReplace: null, 116 | strStartsWith: null, 117 | strStripAccents: null, 118 | strSubstring: 'slice', 119 | strSubstringStart: null, 120 | strToLowerCase: 'downcase', 121 | strToUpperCase: 'upcase', 122 | strToString: null, 123 | strTrim: null, 124 | // ---- number ---- 125 | add: '+', 126 | abs: 'abs', 127 | acos: 'acos', 128 | asin: 'asin', 129 | atan: 'atan', 130 | atan2: null, 131 | ceil: 'ceil', 132 | cos: 'cos', 133 | div: '/', 134 | exp: 'e', 135 | floor: 'floor', 136 | interpolate: 'interpolate', 137 | log: 'ln', 138 | // – : 'ln2' 139 | // – : 'log10' 140 | // – : 'log2' 141 | max: 'max', 142 | min: 'min', 143 | modulo: '%', 144 | mul: '*', 145 | pi: 'pi', 146 | // - : 'e', 147 | pow: '^', 148 | random: null, 149 | rint: null, 150 | round: 'round', 151 | sin: 'sin', 152 | sqrt: 'sqrt', 153 | sub: '-', 154 | tan: 'tan', 155 | toDegrees: null, 156 | toNumber: null, 157 | toRadians: null, 158 | // ---- boolean ---- 159 | all: 'all', 160 | // eslint-disable-next-line id-blacklist 161 | any: 'any', 162 | between: 'within', 163 | double2bool: null, 164 | equalTo: '==', 165 | greaterThan: '>', 166 | greaterThanOrEqualTo: '>=', 167 | in: 'in', 168 | lessThan: '<', 169 | lessThanOrEqualTo: '<=', 170 | not: '!', 171 | notEqualTo: '!=', 172 | parseBoolean: 'to-boolean', 173 | // ---- unknown ---- 174 | case: 'case', 175 | property: 'get', 176 | step: null 177 | }; 178 | 179 | const invertedFunctionNameMap: Partial> = 180 | invert(functionNameMap); 181 | 182 | export function gs2mbExpression(gsExpression?: GeoStylerExpression): 183 | MapboxExpression | T | undefined { 184 | if (!isGeoStylerFunction(gsExpression)) { 185 | return gsExpression as T; 186 | } 187 | 188 | const args = 'args' in gsExpression ? gsExpression.args : []; 189 | 190 | // special handling 191 | switch (gsExpression.name) { 192 | case 'case': { 193 | const mbArgs: any = []; 194 | let fallback: any; 195 | args.forEach((arg: any, index: number) => { 196 | if (index === 0) { 197 | fallback = gs2mbExpression(arg); 198 | return; 199 | } 200 | if (arg.case === null || arg.case === undefined || arg.value === null || arg.value === undefined) { 201 | throw new Error('Could not translate GeoStyler Expression: ' + gsExpression); 202 | } 203 | mbArgs.push(gs2mbExpression(arg.case)); 204 | mbArgs.push(gs2mbExpression(arg.value)); 205 | }); 206 | return ['case', ...mbArgs, fallback]; 207 | } 208 | case 'interpolate': { 209 | const mbArgs: any = []; 210 | args.forEach((arg: any, index: number) => { 211 | if (index === 0) { 212 | mbArgs.push([arg.name]); 213 | return; 214 | } 215 | if (index === 1) { 216 | mbArgs.push(gs2mbExpression(arg)); 217 | return; 218 | } 219 | if (arg.stop === null || arg.stop === undefined || arg.value === null || arg.value === undefined) { 220 | throw new Error('Could not translate GeoStyler Expression: ' + gsExpression); 221 | } 222 | mbArgs.push(gs2mbExpression(arg.stop)); 223 | mbArgs.push(gs2mbExpression(arg.value)); 224 | }); 225 | return ['interpolate', ...mbArgs]; 226 | } 227 | case 'exp': 228 | if (args[0] === 1) { 229 | return ['e']; 230 | } 231 | break; 232 | case 'pi': 233 | return ['pi']; 234 | default: 235 | const mapboxFunctionName = functionNameMap[gsExpression.name]; 236 | if (!mapboxFunctionName) { 237 | throw new Error('Could not translate GeoStyler Expression: ' + gsExpression); 238 | } 239 | return [mapboxFunctionName, ...args.map(arg => gs2mbExpression(arg))] as MapboxExpression; 240 | } 241 | 242 | throw new Error('Could not translate GeoStyler Expression: ' + gsExpression); 243 | 244 | } 245 | 246 | type MbInput = MapboxExpression | PropertyType | StyleFunction; 247 | 248 | export function mb2gsExpression(mbExpression?: MbInput, isColor?: boolean): 249 | GeoStylerExpression | undefined { 250 | 251 | // TODO: is this check valid ? 252 | // fails for arrays like offset = [10, 20] 253 | if (!(Array.isArray(mbExpression) && expressionNames.includes(mbExpression[0]))) { 254 | if (typeof mbExpression === 'string' && isColor) { 255 | return MapboxStyleUtil.getHexColor(mbExpression) as GeoStylerExpression; 256 | } 257 | return mbExpression as GeoStylerExpression | undefined; 258 | } 259 | 260 | const mapboxExpressionName: ExpressionName = mbExpression[0]; 261 | const args: Expression[] = mbExpression.slice(1); 262 | let func: GeoStylerFunction; 263 | 264 | // special handling 265 | switch (mapboxExpressionName) { 266 | case 'e': 267 | func = { 268 | name: 'exp', 269 | args: [1] 270 | }; 271 | break; 272 | case 'case': { 273 | const gsArgs: any[] = []; 274 | const fallback = mb2gsExpression(args.pop(), isColor); 275 | args.forEach((a, index) => { 276 | var gsIndex = Math.floor(index / 2); 277 | if (index % 2 === 0) { 278 | gsArgs[gsIndex] = { 279 | case: mb2gsExpression(a) 280 | }; 281 | } else { 282 | gsArgs[gsIndex] = { 283 | ...gsArgs[gsIndex] as any, 284 | value: mb2gsExpression(a, isColor) 285 | }; 286 | } 287 | }); 288 | // adding the fallback as the first arg 289 | gsArgs.unshift(fallback); 290 | func = { 291 | name: 'case', 292 | args: gsArgs as Fcase['args'] 293 | }; 294 | break; 295 | } 296 | case 'interpolate': { 297 | const interpolationType = (args.shift() as [string])[0]; 298 | const input = mb2gsExpression(args.shift()); 299 | const gsArgs: any[] = []; 300 | 301 | args.forEach((a, index) => { 302 | const gsIndex = Math.floor(index / 2); 303 | if (index % 2 === 0) { 304 | gsArgs[gsIndex] = { 305 | stop: mb2gsExpression(a) 306 | }; 307 | } else { 308 | gsArgs[gsIndex] = { 309 | ...gsArgs[gsIndex] as any, 310 | value: mb2gsExpression(a) 311 | }; 312 | } 313 | }); 314 | // adding the interpolation type and the input as the first args 315 | gsArgs.unshift({name: interpolationType}, input); 316 | func = { 317 | name: 'interpolate', 318 | args: gsArgs as Finterpolate['args'] 319 | }; 320 | break; 321 | } 322 | case 'pi': 323 | func = { 324 | name: 'pi' 325 | }; 326 | break; 327 | default: 328 | const gsFunctionName = invertedFunctionNameMap[mapboxExpressionName]; 329 | if (!gsFunctionName) { 330 | throw new Error('Could not translate Mapbox Expression: ' + mbExpression); 331 | } 332 | func = { 333 | name: gsFunctionName, 334 | args: args.map(arg => mb2gsExpression(arg)) 335 | } as GeoStylerFunction; 336 | break; 337 | } 338 | 339 | return func as GeoStylerExpression; 340 | } 341 | -------------------------------------------------------------------------------- /src/MapboxStyleParser.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import MapboxStyleParser from './MapboxStyleParser'; 3 | 4 | import { beforeEach, expect, it, describe, vi } from 'vitest'; 5 | 6 | import line_simpleline from '../data/styles/line_simpleline'; 7 | import mb_line_simpleline from '../data/mapbox/line_simpleline'; 8 | import mb_line_simpleline_metadata from '../data/mapbox_metadata/line_simpleline'; 9 | import fill_simplefill from '../data/styles/fill_simplefill'; 10 | import mb_fill_simplefill from '../data/mapbox/fill_simplefill'; 11 | import mb_fill_simplefill_metadata from '../data/mapbox_metadata/fill_simplefill'; 12 | import fill_simplefill_outline from '../data/styles/fill_simple_outline'; 13 | import mb_fill_simplefill_outline_metadata from '../data/mapbox_metadata/fill_simple_outline'; 14 | import point_simpletext from '../data/styles/point_simpletext'; 15 | import mb_point_simpletext from '../data/mapbox/point_simpletext'; 16 | import mb_point_simpletext_metadata from '../data/mapbox_metadata/point_simpletext'; 17 | import point_placeholdertext from '../data/styles/point_placeholderText'; 18 | import mb_point_placeholdertext from '../data/mapbox/point_placeholderText'; 19 | import fill_graphic_fill_mark from '../data/styles/fill_graphic_fill_mark'; 20 | import mb_fill_graphic_fill_mark from '../data/mapbox/fill_graphic_fill_mark'; 21 | // import mb_point_placeholdertext_metadata from '../data/mapbox_metadata/point_placeholderText'; 22 | import multi_simpleline_simplefill from '../data/styles/multi_simpleline_simplefill'; 23 | import mb_multi_simpleline_simplefill from '../data/mapbox/multi_simpleline_simplefill'; 24 | import mb_multi_simpleline_simplefill_metadata from '../data/mapbox_metadata/multi_simpleline_simplefill'; 25 | import multi_rule_line_fill from '../data/styles/multi_rule_line_fill'; 26 | // import mb_multi_rule_line_fill from '../data/mapbox/multi_rule_line_fill'; 27 | import mb_multi_rule_line_fill_metadata from '../data/mapbox_metadata/multi_rule_line_fill'; 28 | import line_simpleline_basefilter from '../data/styles/line_simpleline_basefilter'; 29 | import mb_line_simpleline_basefilter from '../data/mapbox/line_simpleline_basefilter'; 30 | import mb_line_simpleline_basefilter_metadata from '../data/mapbox_metadata/line_simpleline_basefilter'; 31 | // import line_simpleline_expression from '../data/styles/line_simpleline_expression'; 32 | // import mb_line_simpleline_expression from '../data/mapbox/line_simpleline_expression'; 33 | // import mb_line_simpleline_expression_metadata from '../data/mapbox_metadata/line_simpleline_expression'; 34 | import line_simpleline_zoom from '../data/styles/line_simpleline_zoom'; 35 | import mb_line_simpleline_zoom from '../data/mapbox/line_simpleline_zoom'; 36 | import mb_line_simpleline_zoom_metadata from '../data/mapbox_metadata/line_simpleline_zoom'; 37 | import icon_simpleicon from '../data/styles/icon_simpleicon'; 38 | import mb_icon_simpleicon from '../data/mapbox/icon_simpleicon'; 39 | import mb_icon_simpleicon_metadata from '../data/mapbox_metadata/icon_simpleicon'; 40 | import icon_simpleicon_mapboxapi from '../data/styles/icon_simpleicon_mapboxapi'; 41 | import mb_icon_simpleicon_mapboxapi from '../data/mapbox/icon_simpleicon_mapboxapi'; 42 | import mb_icon_simpleicon_mapboxapi_metadata from '../data/mapbox_metadata/icon_simpleicon_mapboxapi'; 43 | import circle_simplecircle from '../data/styles/circle_simplecircle'; 44 | import mb_circle_simplecircle from '../data/mapbox/circle_simplecircle'; 45 | import mb_circle_simplecircle_metadata from '../data/mapbox_metadata/circle_simplecircle'; 46 | import fill_patternfill from '../data/styles/fill_patternfill'; 47 | import mb_fill_patternfill from '../data/mapbox/fill_patternfill'; 48 | import mb_fill_patternfill_metadata from '../data/mapbox_metadata/fill_patternfill'; 49 | import line_patternline from '../data/styles/line_patternline'; 50 | import mb_line_patternline from '../data/mapbox/line_patternline'; 51 | import mb_line_patternline_metadata from '../data/mapbox_metadata/line_patternline'; 52 | import point_placeholdertext_simple from '../data/styles/point_placeholderText_simple'; 53 | import mb_point_placeholdertext_simple from '../data/mapbox/point_placeholderText_simple'; 54 | import mb_point_placeholdertext_simple_metadata from '../data/mapbox_metadata/point_placeholderText_simple'; 55 | import icontext_symbolizer_metadata from '../data/styles_metadata/icontext_symbolizer'; 56 | import mb_icontext_symbolizer from '../data/mapbox/icontext_symbolizer'; 57 | import mb_icontext_symbolizer_metadata from '../data/mapbox_metadata/icontext_symbolizer'; 58 | import source_mapping from '../data/styles/source_mapping'; 59 | import mb_source_mapping from '../data/mapbox/source_mapping'; 60 | import mb_source_mapping_metadata from '../data/mapbox_metadata/source_mapping'; 61 | import source_layer_mapping from '../data/styles/source_layer_mapping'; 62 | import mb_source_layer_mapping from '../data/mapbox/source_layer_mapping'; 63 | import mb_source_layer_mapping_metadata from '../data/mapbox_metadata/source_layer_mapping'; 64 | import { CustomLayerInterface } from 'mapbox-gl'; 65 | import { AnyLayer } from 'mapbox-gl'; 66 | import text_placement_line_center from '../data/styles/text_placement_line_center'; 67 | import mb_placement_line_center from '../data/mapbox/text_placement_line_center'; 68 | import mb_text_placement_line_center_metadata from '../data/mapbox_metadata/text_placement_line_center'; 69 | import mb_text_placement_line from '../data/mapbox/text_placement'; 70 | import text_placement_line from '../data/styles/text_placement_line'; 71 | import mb_text_placement_line_metadata from '../data/mapbox_metadata/text_placement_line'; 72 | import mb_text_placement_point from '../data/mapbox/text_placement_point'; 73 | import text_placement_point from '../data/styles/text_placement_point'; 74 | import mb_text_placement_point_metadata from '../data/mapbox_metadata/text_placement_point'; 75 | import color_rgba from '../data/styles/color_rgba'; 76 | import mb_color_rgba from '../data/mapbox/color_rgba'; 77 | 78 | const mockFetchResult = (data: any) => { 79 | vi.spyOn(global, 'fetch') 80 | .mockImplementationOnce(() => { 81 | const response = { 82 | ok: true, 83 | status: 200, 84 | json: () => Promise.resolve(data), 85 | } as Response; 86 | return Promise.resolve(response); 87 | }); 88 | }; 89 | 90 | it('MapboxStyleParser is defined', () => { 91 | expect(MapboxStyleParser).toBeDefined(); 92 | }); 93 | 94 | it('MapboxStyleParser can interpret Mark graphicFill with a fallback', async () => { 95 | expect.assertions(2); 96 | const styleParser = new MapboxStyleParser({replaceGraphicFillWithColor: true}); 97 | const { output: mbStyle } = await styleParser.writeStyle(fill_graphic_fill_mark); 98 | expect(mbStyle).toBeDefined(); 99 | expect(mbStyle).toEqual(mb_fill_graphic_fill_mark); 100 | }); 101 | 102 | describe('MapboxStyleParser implements StyleParser', () => { 103 | let styleParser: MapboxStyleParser; 104 | beforeEach(() => { 105 | styleParser = new MapboxStyleParser(); 106 | }); 107 | describe('#readStyle', () => { 108 | it('can read a mapbox Line style', async () => { 109 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_line_simpleline); 110 | expect(geoStylerStyle).toBeDefined(); 111 | expect(geoStylerStyle).toEqual(line_simpleline); 112 | }); 113 | 114 | it('can read a mapbox Line style with fill pattern', async () => { 115 | mockFetchResult({ 116 | poi: { 117 | width: 12, 118 | height: 12, 119 | x: 0, 120 | y: 0 121 | } 122 | }); 123 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_line_patternline); 124 | expect(geoStylerStyle).toBeDefined(); 125 | expect(geoStylerStyle).toEqual(line_patternline); 126 | }); 127 | it('can read a mapbox text placement line value', async () => { 128 | expect.assertions(2); 129 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_text_placement_line); 130 | expect(geoStylerStyle).toBeDefined(); 131 | expect(geoStylerStyle).toEqual(text_placement_line); 132 | }); 133 | 134 | it('can read a mapbox text placement point value', async () => { 135 | expect.assertions(2); 136 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_text_placement_point); 137 | expect(geoStylerStyle).toBeDefined(); 138 | expect(geoStylerStyle).toEqual(text_placement_point); 139 | }); 140 | 141 | it('can read a mapbox text placement line center value', async () => { 142 | expect.assertions(2); 143 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_placement_line_center); 144 | expect(geoStylerStyle).toBeDefined(); 145 | expect(geoStylerStyle).toEqual(text_placement_line_center); 146 | }); 147 | 148 | it('can read a mapbox Fill style with outline', async () => { 149 | expect.assertions(2); 150 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_fill_simplefill_outline_metadata); 151 | expect(geoStylerStyle).toBeDefined(); 152 | expect(geoStylerStyle).toEqual(fill_simplefill_outline); 153 | }); 154 | 155 | it('can read a mapbox Fill style', async () => { 156 | expect.assertions(2); 157 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_fill_simplefill); 158 | expect(geoStylerStyle).toBeDefined(); 159 | expect(geoStylerStyle).toEqual(fill_simplefill); 160 | }); 161 | 162 | it('can read a mapbox Fill style with fill pattern', async () => { 163 | mockFetchResult({ 164 | poi: { 165 | width: 12, 166 | height: 12, 167 | x: 0, 168 | y: 0 169 | } 170 | }); 171 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_fill_patternfill); 172 | expect(geoStylerStyle).toBeDefined(); 173 | expect(geoStylerStyle).toEqual(fill_patternfill); 174 | }); 175 | 176 | it('can read a mapbox style with a basefilter', async () => { 177 | expect.assertions(2); 178 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_line_simpleline_basefilter); 179 | expect(geoStylerStyle).toBeDefined(); 180 | expect(geoStylerStyle).toEqual(line_simpleline_basefilter); 181 | }); 182 | 183 | // TODO: readd this test once the expressions do work 184 | // it('can read a mapbox style with an expression', async () => { 185 | // expect.assertions(2); 186 | // const { output: geoStylerStyle } = await styleParser.readStyle(mb_line_simpleline_expression); 187 | // expect(geoStylerStyle).toBeDefined(); 188 | // expect(geoStylerStyle).toEqual(line_simpleline_expression); 189 | // }); 190 | 191 | it('can read a mapbox style with min and max zoom', async () => { 192 | // bun does not fully support this yet 193 | // expect.assertions(3); 194 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_line_simpleline_zoom); 195 | expect(geoStylerStyle).toBeDefined(); 196 | const min = geoStylerStyle?.rules[0].scaleDenominator?.min; 197 | const max = geoStylerStyle?.rules[0].scaleDenominator?.max; 198 | const gotMin = line_simpleline_zoom.rules[0].scaleDenominator?.min as number; 199 | const gotMax = line_simpleline_zoom.rules[0].scaleDenominator?.max as number; 200 | expect(min).toBeCloseTo(gotMin, 4); 201 | expect(max).toBeCloseTo(gotMax, 4); 202 | }); 203 | 204 | it('can write and read a mapbox style with min and max zoom', async () => { 205 | // bun does not fully support this yet 206 | // expect.assertions(3); 207 | const { output: mbStyle } = await styleParser.writeStyle(line_simpleline_zoom); 208 | const { output: geoStylerStyle } = await styleParser.readStyle(mbStyle!); 209 | expect(geoStylerStyle).toBeDefined(); 210 | const min = geoStylerStyle?.rules[0].scaleDenominator?.min; 211 | const max = geoStylerStyle?.rules[0].scaleDenominator?.max; 212 | const gotMin = line_simpleline_zoom.rules[0].scaleDenominator?.min as number; 213 | const gotMax = line_simpleline_zoom.rules[0].scaleDenominator?.max as number; 214 | expect(min).toBeCloseTo(gotMin, 4); 215 | expect(max).toBeCloseTo(gotMax, 4); 216 | }); 217 | 218 | it('can read a mapbox Text style', async () => { 219 | expect.assertions(2); 220 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_point_simpletext); 221 | expect(geoStylerStyle).toBeDefined(); 222 | expect(geoStylerStyle).toEqual(point_simpletext); 223 | }); 224 | 225 | it('can read a mapbox Text style with placeholder Text', async () => { 226 | expect.assertions(2); 227 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_point_placeholdertext); 228 | expect(geoStylerStyle).toBeDefined(); 229 | expect(geoStylerStyle).toEqual(point_placeholdertext); 230 | }); 231 | 232 | it('can read a mapbox Text style with formatted Text', async () => { 233 | expect.assertions(2); 234 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_point_placeholdertext_simple); 235 | expect(geoStylerStyle).toBeDefined(); 236 | expect(geoStylerStyle).toEqual(point_placeholdertext_simple); 237 | }); 238 | 239 | it('can read a mapbox Circle style', async () => { 240 | expect.assertions(2); 241 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_circle_simplecircle); 242 | expect(geoStylerStyle).toBeDefined(); 243 | expect(geoStylerStyle).toEqual(circle_simplecircle); 244 | }); 245 | 246 | it('can read a mapbox style with multiple layers', async () => { 247 | expect.assertions(2); 248 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_multi_simpleline_simplefill); 249 | expect(geoStylerStyle).toBeDefined(); 250 | expect(geoStylerStyle).toEqual(multi_simpleline_simplefill); 251 | }); 252 | 253 | it('can read a mapbox style with an icon', async () => { 254 | mockFetchResult({ 255 | poi: { 256 | width: 12, 257 | height: 12, 258 | x: 0, 259 | y: 0 260 | } 261 | }); 262 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_icon_simpleicon); 263 | expect(geoStylerStyle).toBeDefined(); 264 | expect(geoStylerStyle).toEqual(icon_simpleicon); 265 | }); 266 | 267 | it('can read a mapbox style with an icon and resolves mapbox api', async () => { 268 | mockFetchResult({ 269 | poi: { 270 | width: 12, 271 | height: 12, 272 | x: 0, 273 | y: 0 274 | } 275 | }); 276 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_icon_simpleicon_mapboxapi); 277 | expect(geoStylerStyle).toBeDefined(); 278 | expect(geoStylerStyle).toEqual(icon_simpleicon_mapboxapi); 279 | }); 280 | 281 | it('can read a mapbox style with icontext symbolizer', async () => { 282 | mockFetchResult({ 283 | poi: { 284 | width: 12, 285 | height: 12, 286 | x: 0, 287 | y: 0 288 | } 289 | }); 290 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_icontext_symbolizer); 291 | expect(geoStylerStyle).toBeDefined(); 292 | expect(geoStylerStyle).toEqual(icontext_symbolizer_metadata); 293 | }); 294 | 295 | it('can keep track of a mapbox style sources', async () => { 296 | const { output: geoStylerStyle } = await styleParser.readStyle(mb_source_mapping); 297 | expect(geoStylerStyle).toBeDefined(); 298 | expect(geoStylerStyle).toEqual(source_mapping); 299 | }); 300 | 301 | it('can keep track of a mapbox style layer source', async () => { 302 | const { output: geostylerStyle } = await styleParser.readStyle(mb_source_layer_mapping); 303 | expect(geostylerStyle).toBeDefined(); 304 | expect(geostylerStyle).toEqual(source_layer_mapping); 305 | }); 306 | 307 | it('can read a style with rgba color values', async () => { 308 | const { output: geostylerStyle } = await styleParser.readStyle(mb_color_rgba); 309 | expect(geostylerStyle).toBeDefined(); 310 | expect(geostylerStyle).toEqual(color_rgba); 311 | }); 312 | }); 313 | 314 | describe('#writeStyle', () => { 315 | it('can write a mapbox Line style', async () => { 316 | expect.assertions(2); 317 | const { output: mbStyle } = await styleParser.writeStyle(line_simpleline); 318 | expect(mbStyle).toBeDefined(); 319 | expect(mbStyle).toEqual(mb_line_simpleline_metadata); 320 | }); 321 | 322 | it('can write a mapbox Line style with fill pattern', async () => { 323 | expect.assertions(2); 324 | const { output: mbStyle } = await styleParser.writeStyle(line_patternline); 325 | expect(mbStyle).toBeDefined(); 326 | expect(mbStyle).toEqual(mb_line_patternline_metadata); 327 | }); 328 | 329 | it('can write a mapbox Fill style', async () => { 330 | expect.assertions(2); 331 | const { output: mbStyle } = await styleParser.writeStyle(fill_simplefill); 332 | expect(mbStyle).toBeDefined(); 333 | expect(mbStyle).toEqual(mb_fill_simplefill_metadata); 334 | }); 335 | 336 | it('can write a mapbox Fill style with fill pattern', async () => { 337 | expect.assertions(2); 338 | const { output: mbStyle } = await styleParser.writeStyle(fill_patternfill); 339 | expect(mbStyle).toBeDefined(); 340 | expect(mbStyle).toEqual(mb_fill_patternfill_metadata); 341 | }); 342 | 343 | it('can write a mapbox Fill style with outline', async () => { 344 | expect.assertions(2); 345 | const { output: mbStyle } = await styleParser.writeStyle(fill_simplefill_outline); 346 | expect(mbStyle).toBeDefined(); 347 | expect(mbStyle).toEqual(mb_fill_simplefill_outline_metadata); 348 | }); 349 | 350 | it('can write a mapbox Text style', async () => { 351 | expect.assertions(2); 352 | const { output: mbStyle } = await styleParser.writeStyle(point_simpletext); 353 | expect(mbStyle).toBeDefined(); 354 | expect(mbStyle).toEqual(mb_point_simpletext_metadata); 355 | }); 356 | 357 | it('can write a mapbox Text style with a placeholder Text', async () => { 358 | expect.assertions(2); 359 | const { output: mbStyle } = await styleParser.writeStyle(point_placeholdertext_simple); 360 | expect(mbStyle).toBeDefined(); 361 | expect(mbStyle).toEqual(mb_point_placeholdertext_simple_metadata); 362 | }); 363 | 364 | it('can write a mapbox Circle style', async () => { 365 | expect.assertions(2); 366 | const { output: mbStyle } = await styleParser.writeStyle(circle_simplecircle); 367 | expect(mbStyle).toBeDefined(); 368 | expect(mbStyle).toEqual(mb_circle_simplecircle_metadata); 369 | }); 370 | 371 | it('can write a mapbox style with multiple symbolizers', async () => { 372 | expect.assertions(2); 373 | const { output: mbStyle } = await styleParser.writeStyle(multi_simpleline_simplefill); 374 | expect(mbStyle).toBeDefined(); 375 | expect(mbStyle).toEqual(mb_multi_simpleline_simplefill_metadata); 376 | }); 377 | 378 | it('can write a mapbox style with multiple rules', async () => { 379 | expect.assertions(2); 380 | const { output: mbStyle } = await styleParser.writeStyle(multi_rule_line_fill); 381 | expect(mbStyle).toBeDefined(); 382 | expect(mbStyle).toEqual(mb_multi_rule_line_fill_metadata); 383 | }); 384 | 385 | it('can write a mapbox text line placement', async() => { 386 | expect.assertions(2); 387 | const {output: mbStyle} = await styleParser.writeStyle(text_placement_line); 388 | expect(mbStyle).toBeDefined(); 389 | expect(mbStyle).toEqual(mb_text_placement_line_metadata); 390 | }); 391 | it('can write a mapbox text line center placement', async() => { 392 | expect.assertions(2); 393 | const {output: mbStyle} = await styleParser.writeStyle(text_placement_line_center); 394 | expect(mbStyle).toBeDefined(); 395 | expect(mbStyle).toEqual(mb_text_placement_line_center_metadata); 396 | }); 397 | it('can write a mapbox text point placement', async() => { 398 | expect.assertions(2); 399 | const {output: mbStyle} = await styleParser.writeStyle(text_placement_point); 400 | expect(mbStyle).toBeDefined(); 401 | expect(mbStyle).toEqual(mb_text_placement_point_metadata); 402 | }); 403 | it('can write a mapbox style with a complex filter', async () => { 404 | expect.assertions(2); 405 | const { output: mbStyle } = await styleParser.writeStyle(line_simpleline_basefilter); 406 | expect(mbStyle).toBeDefined(); 407 | expect(mbStyle).toEqual(mb_line_simpleline_basefilter_metadata); 408 | }); 409 | 410 | it('can write a mapbox style with min and max zoom', async () => { 411 | // bun does not fully support this yet 412 | // expect.assertions(3); 413 | const { output: mbStyle } = await styleParser.writeStyle(line_simpleline_zoom); 414 | expect(mbStyle).toBeDefined(); 415 | const layer = mbStyle?.layers?.[0] as Exclude; 416 | const mbLayers = mb_line_simpleline_zoom_metadata.layers as (Exclude)[]; 417 | expect(layer.minzoom).toBeCloseTo(mbLayers[0].minzoom!, 0); 418 | expect(layer.maxzoom).toBeCloseTo(mbLayers[0].maxzoom!, 0); 419 | }); 420 | 421 | it('can write a mapbox style with an icon', async () => { 422 | expect.assertions(2); 423 | const { output: mbStyle } = await styleParser.writeStyle(icon_simpleicon); 424 | expect(mbStyle).toBeDefined(); 425 | expect(mbStyle).toEqual(mb_icon_simpleicon_metadata); 426 | }); 427 | 428 | it('can write a mapbox style with an icon and resolves mapbox api', async () => { 429 | expect.assertions(2); 430 | const { output: mbStyle } = await styleParser.writeStyle(icon_simpleicon_mapboxapi); 431 | expect(mbStyle).toBeDefined(); 432 | expect(mbStyle).toEqual(mb_icon_simpleicon_mapboxapi_metadata); 433 | }); 434 | 435 | it('can write a mapbox style with icontext symbolizer', async () => { 436 | const { output: mbStyle } = await styleParser.writeStyle(icontext_symbolizer_metadata); 437 | expect(mbStyle).toBeDefined(); 438 | expect(mbStyle).toEqual(mb_icontext_symbolizer_metadata); 439 | }); 440 | 441 | it('can properly resolve the source mapping', async () => { 442 | const { output: mbStyle } = await styleParser.writeStyle(source_mapping); 443 | expect(mbStyle).toBeDefined(); 444 | expect(mbStyle).toEqual(mb_source_mapping_metadata); 445 | }); 446 | 447 | it('can properly resolve the source layer mapping', async () => { 448 | const { output: mbStyle } = await styleParser.writeStyle(source_layer_mapping); 449 | expect(mbStyle).toBeDefined(); 450 | expect(mbStyle).toEqual(mb_source_layer_mapping_metadata); 451 | }); 452 | }); 453 | }); 454 | --------------------------------------------------------------------------------