├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── build-deploy-publish.yml │ ├── codeql.yml │ └── pr-build-test.yml ├── .gitignore ├── Community.Blazor.MapLibre.Examples ├── App.razor ├── Community.Blazor.MapLibre.Examples.csproj ├── Layout │ ├── MainLayout.razor │ ├── MainLayout.razor.css │ ├── NavMenu.razor │ └── NavMenu.razor.css ├── Pages │ ├── Example.razor │ ├── Examples │ │ ├── AddListener.razor │ │ ├── CreatePopup.razor │ │ ├── CreatePopup.razor.cs │ │ ├── FitBounds.razor │ │ ├── LoadGeoJson.razor │ │ └── RenderGlobe.razor │ └── Home.razor ├── Program.cs ├── Properties │ └── launchSettings.json ├── Shared │ └── GridCard.razor ├── _Imports.razor └── wwwroot │ ├── css │ ├── app.css │ └── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── favicon.png │ ├── icon-192.png │ ├── img │ └── examples │ │ ├── basic.png │ │ ├── fitbounds.png │ │ ├── geojson.png │ │ ├── globe.png │ │ ├── listener.png │ │ └── popup.png │ ├── index.html │ ├── sample-data │ └── weather.json │ └── staticwebapp.config.json ├── Community.Blazor.MapLibre.sln ├── Community.Blazor.MapLibre ├── CallbackHandler.cs ├── Community.Blazor.MapLibre.csproj ├── Converter │ └── OneOfConverter.cs ├── Listener.cs ├── MapLibre.razor ├── MapLibre.razor.cs ├── MapLibre.razor.css ├── MapLibre.razor.js ├── MapOptions.cs ├── Models │ ├── BulkTransaction.cs │ ├── Camera │ │ ├── AnimationOptions.cs │ │ ├── CameraForBoundsOptions.cs │ │ ├── CameraOptions.cs │ │ ├── CenterZoomBearing.cs │ │ ├── EaseToOptions.cs │ │ ├── FitBoundOptions.cs │ │ ├── FlyToOptions.cs │ │ ├── ICameraOptions.cs │ │ ├── ICenterZoomBearing.cs │ │ └── JumpToOptions.cs │ ├── Control │ │ ├── ControlPosition.cs │ │ └── ControlType.cs │ ├── Event │ │ ├── EventType.cs │ │ └── MapMouseEvent.cs │ ├── Feature │ │ ├── FeatureCollection.cs │ │ ├── FeatureFeature.cs │ │ ├── FeatureIdentifier.cs │ │ ├── GeometryType.cs │ │ ├── IFeature.cs │ │ ├── IGeometry.cs │ │ ├── LineGeometry.cs │ │ ├── MultiLineGeometry.cs │ │ ├── MultiPointGeometry.cs │ │ ├── MultiPolygonGeometry.cs │ │ ├── PointGeometry.cs │ │ └── PolygonGeometry.cs │ ├── Image │ │ ├── ImageData.cs │ │ └── StyleImageMetadata.cs │ ├── Layers │ │ ├── BackgroundLayer.cs │ │ ├── CircleLayer.cs │ │ ├── FillExtrusionLayer.cs │ │ ├── FillLayer.cs │ │ ├── HeatMapLayer.cs │ │ ├── HillShadeLayer.cs │ │ ├── Layer.cs │ │ ├── LayerType.cs │ │ ├── LineLayer.cs │ │ ├── RasterLayer.cs │ │ └── SymbolLayer.cs │ ├── LngLat.cs │ ├── LngLatBounds.cs │ ├── Marker │ │ ├── Marker.cs │ │ ├── MarkerAlignment.cs │ │ ├── MarkerAnchor.cs │ │ └── MarkerOptions.cs │ ├── Padding │ │ └── PaddingOptions.cs │ ├── PointLike.cs │ ├── Popup.cs │ ├── PopupOptions.cs │ ├── PositionAnchor.cs │ ├── ProjectionSpecification.cs │ ├── Sources │ │ ├── GeoJsonSource.cs │ │ ├── ISource.cs │ │ ├── ImageSource.cs │ │ ├── QuerySourceFeatureOptions.cs │ │ ├── RasterTileSource.cs │ │ ├── VectorTileSource.cs │ │ └── VideoSource.cs │ ├── Sprite │ │ └── StyleSetterOptions.cs │ ├── TextFit.cs │ ├── Visibility.cs │ └── WebGLContextAttributes.cs ├── _Imports.razor └── wwwroot │ ├── geojson-antimeridian-cut │ ├── LICENSE │ ├── cut.js │ ├── geometry │ │ ├── features.js │ │ ├── lines.js │ │ └── polygons.js │ └── util.js │ ├── maplibre-5.3.0.min.css │ └── maplibre-5.3.0.min.js ├── README.md ├── UNLICENSE └── global.json /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report for Community.Blazor.MapLibre 3 | title: "[BUG]: " 4 | labels: ["bug", "triage"] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for taking the time to fill out this bug report! 11 | 12 | Please provide as much detail as possible to help us understand and reproduce the issue. 13 | 14 | - type: checkboxes 15 | id: prerequisites 16 | attributes: 17 | label: Prerequisites 18 | description: Before submitting a bug report, please confirm the following 19 | options: 20 | - label: I have checked that this issue has not already been reported 21 | required: true 22 | - label: I have tried clearing browser cache and restarting my application 23 | required: true 24 | 25 | - type: input 26 | id: package-version 27 | attributes: 28 | label: Package Version 29 | description: What version of Community.Blazor.MapLibre are you using? 30 | placeholder: "e.g., 5.3.0-beta.1" 31 | validations: 32 | required: true 33 | 34 | - type: input 35 | id: dotnet-version 36 | attributes: 37 | label: .NET Version 38 | description: Which .NET version are you using? 39 | placeholder: "e.g., .NET 9.0" 40 | validations: 41 | required: true 42 | 43 | - type: input 44 | id: browser 45 | attributes: 46 | label: Browser 47 | description: Which browser are you using (if applicable)? 48 | placeholder: "e.g., Chrome 123, Firefox 102, etc." 49 | validations: 50 | required: false 51 | 52 | - type: textarea 53 | id: what-happened 54 | attributes: 55 | label: What happened? 56 | description: | 57 | Please describe the bug in detail. 58 | 59 | Include any error messages or stack traces if available. 60 | placeholder: A clear and concise description of what the bug is. 61 | validations: 62 | required: true 63 | 64 | - type: textarea 65 | id: expected-behavior 66 | attributes: 67 | label: Expected behavior 68 | description: What did you expect to happen? 69 | placeholder: A clear and concise description of what you expected to happen. 70 | validations: 71 | required: true 72 | 73 | - type: textarea 74 | id: reproduction 75 | attributes: 76 | label: Reproduction steps 77 | description: | 78 | How can we reproduce this issue? Provide a step-by-step guide or a minimal code sample. 79 | placeholder: | 80 | 1. Add MapLibre to the page with '...' 81 | 2. Configure options '...' 82 | 3. Call method '...' 83 | 4. See error 84 | render: markdown 85 | validations: 86 | required: true 87 | 88 | - type: textarea 89 | id: code-sample 90 | attributes: 91 | label: Code Sample 92 | description: | 93 | Please provide a code sample that demonstrates the issue. 94 | This will be automatically formatted into code, so no need for backticks. 95 | render: csharp 96 | validations: 97 | required: false 98 | 99 | - type: textarea 100 | id: additional-info 101 | attributes: 102 | label: Additional Information 103 | description: | 104 | Add any other context about the problem here. 105 | Screenshots, logs, or references to related issues are helpful. 106 | validations: 107 | required: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for Community.Blazor.MapLibre 3 | title: "[FEATURE]: " 4 | labels: ["enhancement"] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for suggesting a new feature! 11 | 12 | Please provide as much detail as possible to help us understand your idea. 13 | 14 | - type: textarea 15 | id: problem-description 16 | attributes: 17 | label: Is your feature request related to a problem? 18 | description: | 19 | A clear and concise description of what the problem is. 20 | placeholder: | 21 | I'm always frustrated when [...] or I need a way to [...] 22 | validations: 23 | required: false 24 | 25 | - type: textarea 26 | id: solution-description 27 | attributes: 28 | label: Describe the solution you'd like 29 | description: | 30 | A clear and concise description of what you want to happen. 31 | 32 | If you know of similar implementations in other libraries or languages, please reference them. 33 | placeholder: | 34 | I would like to be able to [...] so that [...] 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | id: alternatives 40 | attributes: 41 | label: Describe alternatives you've considered 42 | description: | 43 | Have you considered any alternative solutions or workarounds? 44 | placeholder: | 45 | I've tried implementing this with [...] but it doesn't work well because [...] 46 | validations: 47 | required: false 48 | 49 | - type: textarea 50 | id: maplibre-js-reference 51 | attributes: 52 | label: MapLibre JS Reference 53 | description: | 54 | If this feature exists in MapLibre JS, please provide links to the relevant documentation or examples. 55 | placeholder: | 56 | This feature corresponds to [method/property] in MapLibre JS: [link to documentation] 57 | validations: 58 | required: false 59 | 60 | - type: textarea 61 | id: example-usage 62 | attributes: 63 | label: Example Usage 64 | description: | 65 | If possible, provide an example of how you envision using this feature in code. 66 | placeholder: | 67 | ```csharp 68 | // Example of how the feature might be used 69 | var map = new MapLibre(); 70 | map.NewFeature(...); 71 | ``` 72 | render: markdown 73 | validations: 74 | required: false 75 | 76 | - type: textarea 77 | id: additional-context 78 | attributes: 79 | label: Additional context 80 | description: | 81 | Add any other context, screenshots, diagrams or references about the feature request here. 82 | validations: 83 | required: false 84 | 85 | - type: checkboxes 86 | id: contribution 87 | attributes: 88 | label: Contribution 89 | description: Would you be willing to contribute this feature? 90 | options: 91 | - label: I'm interested in implementing this feature with guidance -------------------------------------------------------------------------------- /.github/workflows/build-deploy-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build, Test, and Publish Multi-Project Solution 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | - "release/*" # For library publishing 8 | workflow_dispatch: # Allow manual triggering of the workflow 9 | 10 | jobs: 11 | # Job 1: Build and Test Both Projects 12 | build: 13 | name: Build, Test, and Package Library 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | # Step 1: Checkout the repository 18 | - name: Checkout repository 19 | uses: actions/checkout@v3 20 | 21 | # Step 2: Setup .NET 22 | - name: Setup .NET 23 | uses: actions/setup-dotnet@v3 24 | with: 25 | dotnet-version: 9.0.x 26 | 27 | # Step 3: Restore dependencies 28 | - name: Restore dependencies 29 | run: dotnet restore 30 | 31 | # Step 4: Build both projects 32 | - name: Build Library and Examples 33 | run: dotnet build --no-restore -c Release /p:ContinuousIntegrationBuild=true 34 | 35 | # Step 5: Test the Library Project (Optional If You Only Want to Test Library) 36 | - name: Test Library 37 | run: dotnet test Community.Blazor.MapLibre/Community.Blazor.MapLibre.csproj --verbosity normal -c Release 38 | 39 | # Step 6: Pack the Library for NuGet 40 | - name: Pack NuGet Library 41 | if: ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref_name, 'release')) 42 | run: dotnet pack Community.Blazor.MapLibre/Community.Blazor.MapLibre.csproj -c Release --no-build --output nuget 43 | # Only pack `Community.Blazor.MapLibre.csproj` for NuGet, not the examples 44 | 45 | # Step 7: Upload NuGet packages as artifact 46 | - name: Upload NuGet Artifact 47 | if: ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref_name, 'release')) 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: nuget 51 | path: nuget/ 52 | 53 | # Step 8: Prepare Blazor examples for deployment 54 | - name: Publish Blazor Examples 55 | if: ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref_name, 'main')) 56 | run: dotnet publish Community.Blazor.MapLibre.Examples/Community.Blazor.MapLibre.Examples.csproj -c Release -o blazor_build_output 57 | 58 | # Step 9: Upload Blazor artifacts for deployment 59 | - name: Upload Blazor Deployment Artifact 60 | if: ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref_name, 'main')) 61 | uses: actions/upload-pages-artifact@v3 62 | with: 63 | path: blazor_build_output/wwwroot 64 | 65 | # Job 2: Publish NuGet Package 66 | publish: 67 | name: Publish Library to NuGet 68 | runs-on: ubuntu-latest 69 | needs: [build] # Requires the Build job to complete 70 | 71 | if: > 72 | ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref_name, 'release')) 73 | 74 | steps: 75 | # Step 1: Download NuGet package artifact from the Build job 76 | - name: Download NuGet Artifact 77 | uses: actions/download-artifact@v4 78 | with: 79 | name: nuget 80 | path: nuget 81 | 82 | # Step 2: Setup .NET in the Publish job 83 | - name: Setup .NET 84 | uses: actions/setup-dotnet@v3 85 | with: 86 | dotnet-version: 9.0.x 87 | 88 | # Step 3: Publish the library to NuGet (using GitHub token or API key) 89 | - name: Publish to NuGet 90 | run: dotnet nuget push nuget/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate 91 | 92 | # Job 3: Deploy Blazor Examples 93 | deploy: 94 | name: Deploy Blazor Examples 95 | runs-on: ubuntu-latest 96 | needs: [build] # Requires the Build job to complete 97 | 98 | if: > 99 | ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref_name, 'main')) 100 | 101 | permissions: 102 | pages: write # Grant GitHub Pages deploy permissions 103 | id-token: write # To verify the deployment source 104 | 105 | environment: 106 | name: github-pages 107 | url: ${{ steps.deployment.outputs.page_url }} 108 | 109 | steps: 110 | - name: Deploy to GitHub Pages 111 | id: deployment 112 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL Advanced" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '42 10 * * 6' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | permissions: 32 | # required for all workflows 33 | security-events: write 34 | 35 | # required to fetch internal or private CodeQL packs 36 | packages: read 37 | 38 | # only required for workflows in private repositories 39 | actions: read 40 | contents: read 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | include: 46 | - language: actions 47 | build-mode: none 48 | - language: csharp 49 | build-mode: none 50 | - language: javascript-typescript 51 | build-mode: none 52 | # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 53 | # Use `c-cpp` to analyze code written in C, C++ or both 54 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 55 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 56 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 57 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 58 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 59 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 60 | steps: 61 | - name: Checkout repository 62 | uses: actions/checkout@v4 63 | 64 | # Add any setup steps before running the `github/codeql-action/init` action. 65 | # This includes steps like installing compilers or runtimes (`actions/setup-node` 66 | # or others). This is typically only required for manual builds. 67 | # - name: Setup runtime (example) 68 | # uses: actions/setup-example@v1 69 | 70 | # Initializes the CodeQL tools for scanning. 71 | - name: Initialize CodeQL 72 | uses: github/codeql-action/init@v3 73 | with: 74 | languages: ${{ matrix.language }} 75 | build-mode: ${{ matrix.build-mode }} 76 | # If you wish to specify custom queries, you can do so here or in a config file. 77 | # By default, queries listed here will override any specified in a config file. 78 | # Prefix the list here with "+" to use these queries and those in the config file. 79 | 80 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 81 | # queries: security-extended,security-and-quality 82 | 83 | # If the analyze step fails for one of the languages you are analyzing with 84 | # "We were unable to automatically build your code", modify the matrix above 85 | # to set the build mode to "manual" for that language. Then modify this step 86 | # to build your code. 87 | # ℹ️ Command-line programs to run using the OS shell. 88 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 89 | - if: matrix.build-mode == 'manual' 90 | shell: bash 91 | run: | 92 | echo 'If you are using a "manual" build mode for one or more of the' \ 93 | 'languages you are analyzing, replace this with the commands to build' \ 94 | 'your code, for example:' 95 | echo ' make bootstrap' 96 | echo ' make release' 97 | exit 1 98 | 99 | - name: Perform CodeQL Analysis 100 | uses: github/codeql-action/analyze@v3 101 | with: 102 | category: "/language:${{matrix.language}}" 103 | -------------------------------------------------------------------------------- /.github/workflows/pr-build-test.yml: -------------------------------------------------------------------------------- 1 | name: PR Build and Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | - "release/*" 8 | 9 | jobs: 10 | pr-build: 11 | name: Build and Test 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v3 20 | with: 21 | dotnet-version: 9.0.x 22 | 23 | - name: Restore dependencies 24 | run: dotnet restore 25 | 26 | - name: Build projects 27 | run: dotnet build --no-restore -c Release 28 | 29 | - name: Test Library 30 | run: dotnet test Community.Blazor.MapLibre/Community.Blazor.MapLibre.csproj --verbosity normal -c Release -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ 6 | 7 | .idea/ -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
-------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Community.Blazor.MapLibre.Examples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | Community.Blazor.MapLibre.Examples.WebAssembly 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 |
3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 |
-------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 | 22 | 23 | @code { 24 | private bool collapseNavMenu = true; 25 | 26 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 27 | 28 | private void ToggleNavMenu() 29 | { 30 | collapseNavMenu = !collapseNavMenu; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .bi { 15 | display: inline-block; 16 | position: relative; 17 | width: 1.25rem; 18 | height: 1.25rem; 19 | margin-right: 0.75rem; 20 | top: -1px; 21 | background-size: cover; 22 | } 23 | 24 | .bi-house-door-fill-nav-menu { 25 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 26 | } 27 | 28 | .bi-plus-square-fill-nav-menu { 29 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 30 | } 31 | 32 | .bi-list-nested-nav-menu { 33 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 34 | } 35 | 36 | .nav-item { 37 | font-size: 0.9rem; 38 | padding-bottom: 0.5rem; 39 | } 40 | 41 | .nav-item:first-of-type { 42 | padding-top: 1rem; 43 | } 44 | 45 | .nav-item:last-of-type { 46 | padding-bottom: 1rem; 47 | } 48 | 49 | .nav-item ::deep a { 50 | color: #d7d7d7; 51 | border-radius: 4px; 52 | height: 3rem; 53 | display: flex; 54 | align-items: center; 55 | line-height: 3rem; 56 | } 57 | 58 | .nav-item ::deep a.active { 59 | background-color: rgba(255,255,255,0.37); 60 | color: white; 61 | } 62 | 63 | .nav-item ::deep a:hover { 64 | background-color: rgba(255,255,255,0.1); 65 | color: white; 66 | } 67 | 68 | @media (min-width: 641px) { 69 | .navbar-toggler { 70 | display: none; 71 | } 72 | 73 | .collapse { 74 | /* Never collapse the sidebar for wide screens */ 75 | display: block; 76 | } 77 | 78 | .nav-scrollable { 79 | /* Allow sidebar to scroll for tall menus */ 80 | height: calc(100vh - 3.5rem); 81 | overflow-y: auto; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Example.razor: -------------------------------------------------------------------------------- 1 | @page "/Example" 2 | @using Community.Blazor.MapLibre.Examples.WebAssembly.Shared 3 | 4 |

Examples

5 |

6 | This is list of few example usages of the Blazor.MapLibre 7 |

8 |
9 | 10 | @foreach (var item in _samplesList) 11 | { 12 | 16 | } 17 | 18 |
19 | 20 | 21 | @code 22 | { 23 | private readonly List _samplesList = 24 | [ 25 | new() { Title = "Load GeoJSON", Url = "Examples/LoadGeoJson", ImageUrl = "/img/examples/geojson.png"}, 26 | new() { Title = "Add Listener", Url = "Examples/AddListener", ImageUrl = "/img/examples/listener.png" }, 27 | new() { Title = "Fit Bounds", Url = "Examples/FitBounds", ImageUrl = "/img/examples/fitbounds.png" }, 28 | new() { Title = "Create Popup", Url = "Examples/CreatePopup", ImageUrl = "/img/examples/popup.png" }, 29 | new() { Title = "Render Globe", Url = "Examples/RenderGlobe", ImageUrl = "/img/examples/globe.png" } 30 | ]; 31 | 32 | // ExampleItem class to store information 33 | private class MapSampleItem 34 | { 35 | public string Title { get; set; } = string.Empty; 36 | public string Url { get; set; } = string.Empty; 37 | public string ImageUrl { get; set; } = string.Empty; 38 | public string? Class { get; set; } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Examples/AddListener.razor: -------------------------------------------------------------------------------- 1 | @page "/Examples/AddListener" 2 | @using System.Text.Json 3 | @using Community.Blazor.MapLibre.Models 4 |

AddListener

5 | 6 |
7 |
8 | 10 |
11 |
12 | 13 | <Map @ref="_map" OnLoad="HandleMapLoad" ></Map> 14 | 15 |
16 | 
17 | @code
18 | {
19 |     private Map _map { get; set; } = new Map();
20 |     private readonly MapOptions _mapOptions = new()
21 |     {
22 |         Style = "https://demotiles.maplibre.org/style.json"
23 |     };
24 |     private async Task HandleMapLoad(EventArgs args)
25 |     {
26 |         // Add map listeners after it has loaded
27 |         await _map.AddListener<dynamic>("click", OnClick);
28 |     }
29 | 
30 |     public void OnClick(dynamic eventData)
31 |     {
32 |         Console.WriteLine(JsonSerializer.Serialize(eventData));
33 |         Console.WriteLine("Layer has been clicked");
34 |     }
35 | }
36 | 
37 | 38 |
39 |
40 |
41 | 42 | @code { 43 | private MapLibre _mapListener { get; set; } = new MapLibre(); 44 | private readonly MapOptions _mapOptions = new() 45 | { 46 | Style = "https://demotiles.maplibre.org/style.json" 47 | }; 48 | private async Task HandleMapLoadListener(EventArgs args) 49 | { 50 | await _mapListener.AddListener("click", OnClick); 51 | } 52 | public void OnClick(dynamic eventData) 53 | { 54 | Console.WriteLine(JsonSerializer.Serialize(eventData)); 55 | Console.WriteLine("Layer has been clicked"); 56 | } 57 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Examples/CreatePopup.razor: -------------------------------------------------------------------------------- 1 | @page "/Examples/CreatePopup" 2 | @using Community.Blazor.MapLibre.Models 3 | @using Community.Blazor.MapLibre.Models.Event 4 | 5 |

Create Popup

6 | 7 | 12 | 13 | 14 | @code 15 | { 16 | private MapLibre? _map { get; set; } 17 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Examples/CreatePopup.razor.cs: -------------------------------------------------------------------------------- 1 | using Community.Blazor.MapLibre.Models; 2 | using Community.Blazor.MapLibre.Models.Event; 3 | 4 | namespace Community.Blazor.MapLibre.Examples.WebAssembly.Pages.Examples; 5 | 6 | public partial class CreatePopup 7 | { 8 | private readonly MapOptions _mapOptions = new() 9 | { 10 | Style = "https://demotiles.maplibre.org/style.json" 11 | }; 12 | 13 | private async Task OnMapLoad(EventArgs args) 14 | { 15 | if (_map is null) 16 | { 17 | return; 18 | } 19 | 20 | await _map.OnClick(null, e => AddPopup(e)); 21 | 22 | async void AddPopup(MapMouseEvent evnt) 23 | { 24 | await _map.CreatePopup(new Popup 25 | { 26 | Content = $""" 27 |
28 |

Map clicked

29 |

You clicked the map at {evnt.LngLat.Latitude} {evnt.LngLat.Longitude}

30 |
31 | """, 32 | Coordinates = evnt.LngLat 33 | }, new PopupOptions 34 | { 35 | CloseOnClick = true, 36 | CloseOnMove = true, 37 | FocusAfterOpen = true, 38 | }); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Examples/FitBounds.razor: -------------------------------------------------------------------------------- 1 | @page "/Examples/FitBounds" 2 | @using Community.Blazor.MapLibre.Models 3 |

FitBounds

4 | 5 |
6 |
7 | 9 |
10 |

Fit Bounds

11 |
12 | 13 | @code { 14 | private MapLibre _mapListener { get; set; } = new MapLibre(); 15 | private readonly MapOptions _mapOptions = new() 16 | { 17 | Style = "https://demotiles.maplibre.org/style.json" 18 | }; 19 | private async Task HandleMapLoadListener(EventArgs args) 20 | { 21 | var bounds = new LngLatBounds() 22 | { 23 | Southwest = new LngLat(17.124277, 48.154861), 24 | Northeast = new LngLat(21.262986, 49.059367) 25 | }; 26 | 27 | await _mapListener.FitBounds(bounds); 28 | } 29 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Examples/LoadGeoJson.razor: -------------------------------------------------------------------------------- 1 | @page "/Examples/LoadGeoJson" 2 | @using Community.Blazor.MapLibre.Models 3 | @using Community.Blazor.MapLibre.Models.Feature 4 | @using Community.Blazor.MapLibre.Models.Layers 5 | @using Community.Blazor.MapLibre.Models.Sources 6 |

LoadGeoJson

7 | 8 |
9 |
10 | 12 |
13 |

Add GEOJson

14 |
15 | 16 | 17 |
 18 | <MapLibre @ref="_mapWithGeoJson" Options="_mapWithGeoJsonOptions" OnLoad="HandleMapLoadGeoJson" ></MapLibre>
 19 | @@code {
 20 |     private Map _mapWithGeoJson { get; set; } = new Map();
 21 |     private readonly MapOptions _mapWithGeoJsonOptions = new MapOptions()
 22 |     {
 23 |         Style = "https://demotiles.maplibre.org/style.json",
 24 |         Center = new LngLat()
 25 |         {
 26 |             Latitude = 45.188193,
 27 |             Longitude = -68.991841
 28 |         },
 29 |         Zoom = 4
 30 |     };
 31 |     private async Task HandleMapLoadGeoJson(EventArgs args)
 32 |     {
 33 |         await _mapWithGeoJson.AddSource("maine", new GeoJsonSource()
 34 |         {
 35 |             Data = new FeatureFeature()
 36 |             {
 37 |                 Geometry = new PolygonFeature()
 38 |                 {
 39 |                     Coordinates =
 40 |                     [
 41 |                         [
 42 |                             [-67.13734351262877, 45.137451890638886],
 43 |                             [-66.96466, 44.8097],
 44 |                             [-68.03252, 44.3252],
 45 |                             [-69.06, 43.98],
 46 |                             [-70.11617, 43.68405],
 47 |                             [-70.64573401557249, 43.090083319667144],
 48 |                             [-70.75102474636725, 43.08003225358635],
 49 |                             [-70.79761105007827, 43.21973948828747],
 50 |                             [-70.98176001655037, 43.36789581966826],
 51 |                             [-70.94416541205806, 43.46633942318431],
 52 |                             [-71.08482, 45.3052400000002],
 53 |                             [-70.6600225491012, 45.46022288673396],
 54 |                             [-70.30495378282376, 45.914794623389355],
 55 |                             [-70.00014034695016, 46.69317088478567],
 56 |                             [-69.23708614772835, 47.44777598732787],
 57 |                             [-68.90478084987546, 47.184794623394396],
 58 |                             [-68.23430497910454, 47.35462921812177],
 59 |                             [-67.79035274928509, 47.066248887716995],
 60 |                             [-67.79141211614706, 45.702585354182816],
 61 |                             [-67.13734351262877, 45.137451890638886],
 62 |                         ]
 63 |                     ]
 64 |                 }
 65 |             }
 66 |         });
 67 | 
 68 |         await _mapWithGeoJson.AddLayer(new FillLayer()
 69 |         {
 70 |             Id = "GeoJsonLayerId",
 71 |             Source = "maine",
 72 |             Paint = new FillLayerPaint()
 73 |             {
 74 |                 FillColor = "#088",
 75 |                 FillOpacity = 0.8
 76 |             }
 77 |         });
 78 |     }
 79 | }
 80 |             
81 |
82 |
83 |
84 | 85 | @code { 86 | private MapLibre _mapWithGeoJson { get; set; } = new MapLibre(); 87 | private readonly MapOptions _mapWithGeoJsonOptions = new MapOptions() 88 | { 89 | Style = "https://demotiles.maplibre.org/style.json", 90 | Center = new LngLat 91 | { 92 | Latitude = 45.188193, 93 | Longitude = -68.991841 94 | }, 95 | Zoom = 4 96 | }; 97 | private async Task HandleMapLoadGeoJson(EventArgs args) 98 | { 99 | await _mapWithGeoJson.AddSource("maine", new GeoJsonSource() 100 | { 101 | Data = new FeatureFeature() 102 | { 103 | Geometry = new PolygonGeometry() 104 | { 105 | Coordinates = 106 | [ 107 | [ 108 | [-67.13734351262877, 45.137451890638886], 109 | [-66.96466, 44.8097], 110 | [-68.03252, 44.3252], 111 | [-69.06, 43.98], 112 | [-70.11617, 43.68405], 113 | [-70.64573401557249, 43.090083319667144], 114 | [-70.75102474636725, 43.08003225358635], 115 | [-70.79761105007827, 43.21973948828747], 116 | [-70.98176001655037, 43.36789581966826], 117 | [-70.94416541205806, 43.46633942318431], 118 | [-71.08482, 45.3052400000002], 119 | [-70.6600225491012, 45.46022288673396], 120 | [-70.30495378282376, 45.914794623389355], 121 | [-70.00014034695016, 46.69317088478567], 122 | [-69.23708614772835, 47.44777598732787], 123 | [-68.90478084987546, 47.184794623394396], 124 | [-68.23430497910454, 47.35462921812177], 125 | [-67.79035274928509, 47.066248887716995], 126 | [-67.79141211614706, 45.702585354182816], 127 | [-67.13734351262877, 45.137451890638886], 128 | ] 129 | ] 130 | } 131 | } 132 | }); 133 | 134 | await _mapWithGeoJson.AddLayer(new FillLayer() 135 | { 136 | Id = "GeoJsonLayerId", 137 | Source = "maine", 138 | Paint = new FillLayerPaint() 139 | { 140 | FillColor = "#088", 141 | FillOpacity = 0.8 142 | } 143 | }); 144 | } 145 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Examples/RenderGlobe.razor: -------------------------------------------------------------------------------- 1 | @page "/Examples/RenderGlobe" 2 | @using Community.Blazor.MapLibre.Models 3 | 4 |

Render Globe

5 | 6 |
7 |
8 | 13 | 14 |
15 |
16 | 17 | 18 | 19 | @code { 20 | private MapLibre _mapListener { get; set; } = new MapLibre(); 21 | 22 | private readonly MapOptions _mapOptions = new() 23 | { 24 | Style = "https://demotiles.maplibre.org/style.json" 25 | }; 26 | 27 | 28 | private async Task OnStyleLoad(EventArgs args) 29 | { 30 | await _mapListener.SetProjection(new ProjectionSpecification() 31 | { 32 | Type = "globe" 33 | }); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using Community.Blazor.MapLibre.Models 3 | 4 |

Introduction

5 |

6 | Community.Blazor.Maplibre is a wrapper around MapLibre-GL-JS library 7 |

8 |
9 |
10 |
11 | 13 |
14 |

Basic map

15 |
16 | 17 |
18 | <MapLibre Options="_mapOptions" Class="rounded-top"
19 |      Height="300px"></MapLibre>
20 | 
21 | @@code
22 | {
23 |     private readonly MapOptions _mapOptions = new()
24 |     {
25 |         Container = "UniqueMapId",
26 |         Style = "https://demotiles.maplibre.org/style.json",
27 |         Center = new LngLat()
28 |         {
29 |             Latitude = -2.320679,
30 |             Longitude = 112.772537
31 |         },
32 |         Zoom = 3
33 |     };
34 | }
35 | 
36 |
37 |
38 |
39 |
40 | 41 | 42 | @code 43 | { 44 | private readonly MapOptions _mapOptions = new() 45 | { 46 | Container = "UniqueMapId", 47 | Style = "https://demotiles.maplibre.org/style.json", 48 | Center = new LngLat() 49 | { 50 | Latitude = -2.320679, 51 | Longitude = 112.772537 52 | }, 53 | Zoom = 3 54 | }; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Web; 2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 3 | using Community.Blazor.MapLibre.Examples.WebAssembly; 4 | 5 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 6 | builder.RootComponents.Add("#app"); 7 | builder.RootComponents.Add("head::after"); 8 | 9 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 10 | 11 | await builder.Build().RunAsync(); -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:20456", 8 | "sslPort": 44353 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5048", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7278;http://localhost:5048", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/Shared/GridCard.razor: -------------------------------------------------------------------------------- 1 | @inject NavigationManager NavigationManager 2 | 3 |
4 |
5 | 6 |
7 |

@Title

8 |
9 | 10 | @code { 11 | 12 | /// 13 | /// The title to be displayed in the card. 14 | /// 15 | [Parameter] 16 | public string? Title { get; set; } 17 | 18 | /// 19 | /// Optional CSS class names. If given, these will be included in the class attribute of the component. 20 | /// 21 | [Parameter] 22 | public virtual string? Class { get; set; } = null; 23 | 24 | [Parameter] 25 | public string ImageUrl { get; set; } = "https://placehold.co/600x200/png"; 26 | 27 | /// 28 | /// The target URL for navigation when the card is clicked. 29 | /// 30 | [Parameter] 31 | public string? TargetUrl { get; set; } 32 | 33 | /// 34 | /// Handles the click event on the card and navigates to the specified TargetUrl if it is not null or empty. 35 | /// 36 | private void NavigateToPage() 37 | { 38 | if (!string.IsNullOrEmpty(TargetUrl)) 39 | { 40 | NavigationManager.NavigateTo(TargetUrl); // Navigate to the specified URL 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using Community.Blazor.MapLibre.Examples.WebAssembly 10 | @using Community.Blazor.MapLibre.Examples.WebAssembly.Layout -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | h1:focus { 6 | outline: none; 7 | } 8 | 9 | a, .btn-link { 10 | color: #0071c1; 11 | } 12 | 13 | .btn-primary { 14 | color: #fff; 15 | background-color: #1b6ec2; 16 | border-color: #1861ac; 17 | } 18 | 19 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 20 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 21 | } 22 | 23 | .content { 24 | padding-top: 1.1rem; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid red; 33 | } 34 | 35 | .validation-message { 36 | color: red; 37 | } 38 | 39 | #blazor-error-ui { 40 | background: lightyellow; 41 | bottom: 0; 42 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 43 | display: none; 44 | left: 0; 45 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 46 | position: fixed; 47 | width: 100%; 48 | z-index: 1000; 49 | } 50 | 51 | #blazor-error-ui .dismiss { 52 | cursor: pointer; 53 | position: absolute; 54 | right: 0.75rem; 55 | top: 0.5rem; 56 | } 57 | 58 | .blazor-error-boundary { 59 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 60 | padding: 1rem 1rem 1rem 3.7rem; 61 | color: white; 62 | } 63 | 64 | .blazor-error-boundary::after { 65 | content: "An error has occurred." 66 | } 67 | 68 | .loading-progress { 69 | position: relative; 70 | display: block; 71 | width: 8rem; 72 | height: 8rem; 73 | margin: 20vh auto 1rem auto; 74 | } 75 | 76 | .loading-progress circle { 77 | fill: none; 78 | stroke: #e0e0e0; 79 | stroke-width: 0.6rem; 80 | transform-origin: 50% 50%; 81 | transform: rotate(-90deg); 82 | } 83 | 84 | .loading-progress circle:last-child { 85 | stroke: #1b6ec2; 86 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; 87 | transition: stroke-dasharray 0.05s ease-in-out; 88 | } 89 | 90 | .loading-progress-text { 91 | position: absolute; 92 | text-align: center; 93 | font-weight: bold; 94 | inset: calc(20vh + 3.25rem) 0 auto 0.2rem; 95 | } 96 | 97 | .loading-progress-text:after { 98 | content: var(--blazor-load-percentage-text, "Loading"); 99 | } 100 | 101 | code { 102 | color: #c02d76; 103 | } 104 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/favicon.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/icon-192.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/img/examples/basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/img/examples/basic.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/img/examples/fitbounds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/img/examples/fitbounds.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/img/examples/geojson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/img/examples/geojson.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/img/examples/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/img/examples/globe.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/img/examples/listener.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/img/examples/listener.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/img/examples/popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre.Examples/wwwroot/img/examples/popup.png -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Community.Blazor.MapLibre.Examples 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 |
31 | An unhandled error has occurred. 32 | Reload 33 | 🗙 34 |
35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.Examples/wwwroot/staticwebapp.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationFallback": { 3 | "rewrite": "/index.html" 4 | } 5 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Community.Blazor.MapLibre", "Community.Blazor.MapLibre\Community.Blazor.MapLibre.csproj", "{3DA04D2B-0D7F-428F-86B7-C4ACBCE2BA56}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Community.Blazor.MapLibre.Examples", "Community.Blazor.MapLibre.Examples\Community.Blazor.MapLibre.Examples.csproj", "{44214551-FA9D-4809-87EA-7843BDB18397}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "items", "items", "{FB2BD75E-3038-46E3-B3D9-851ADD3E045B}" 8 | ProjectSection(SolutionItems) = preProject 9 | global.json = global.json 10 | README.md = README.md 11 | UNLICENSE = UNLICENSE 12 | EndProjectSection 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{1041976B-4110-4B6A-9130-0144068C0638}" 15 | ProjectSection(SolutionItems) = preProject 16 | .github\workflows\build-deploy-publish.yml = .github\workflows\build-deploy-publish.yml 17 | .github\workflows\pr-build-test.yml = .github\workflows\pr-build-test.yml 18 | .github\workflows\codeql.yml = .github\workflows\codeql.yml 19 | EndProjectSection 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Release|Any CPU = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {3DA04D2B-0D7F-428F-86B7-C4ACBCE2BA56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {3DA04D2B-0D7F-428F-86B7-C4ACBCE2BA56}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {3DA04D2B-0D7F-428F-86B7-C4ACBCE2BA56}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {3DA04D2B-0D7F-428F-86B7-C4ACBCE2BA56}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {44214551-FA9D-4809-87EA-7843BDB18397}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {44214551-FA9D-4809-87EA-7843BDB18397}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {44214551-FA9D-4809-87EA-7843BDB18397}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {44214551-FA9D-4809-87EA-7843BDB18397}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {1041976B-4110-4B6A-9130-0144068C0638} = {FB2BD75E-3038-46E3-B3D9-851ADD3E045B} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/CallbackHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json; 3 | 4 | namespace Community.Blazor.MapLibre; 5 | 6 | /// 7 | /// Represents a callback action to handle JavaScript events in C#. 8 | /// 9 | public class CallbackHandler 10 | { 11 | private readonly IJSObjectReference _jsRuntimeReference; 12 | private readonly string _eventType; 13 | private readonly Delegate _callbackDelegate; 14 | private readonly Type? _argumentType; 15 | 16 | /// 17 | /// Constructor for initializing a callback handler with arguments. 18 | /// 19 | /// Reference to the JavaScript runtime object. 20 | /// Type of the event (e.g., click, drag). 21 | /// The C# delegate to invoke when the event is triggered. 22 | /// The type of the argument expected for the callback delegate. 23 | public CallbackHandler(IJSObjectReference jsRuntimeReference, string eventType, Delegate callbackDelegate, Type argumentType) 24 | { 25 | _jsRuntimeReference = jsRuntimeReference; 26 | _eventType = eventType; 27 | _callbackDelegate = callbackDelegate; 28 | _argumentType = argumentType; 29 | } 30 | 31 | /// 32 | /// Constructor for initializing a callback handler without arguments. 33 | /// 34 | /// Reference to the JavaScript runtime object. 35 | /// Type of the event (e.g., click, drag). 36 | /// The C# delegate to invoke when the event is triggered. 37 | public CallbackHandler(IJSObjectReference jsRuntimeReference, string eventType, Delegate callbackDelegate) 38 | { 39 | _jsRuntimeReference = jsRuntimeReference; 40 | _eventType = eventType; 41 | _callbackDelegate = callbackDelegate; 42 | } 43 | 44 | /// 45 | /// Removes the event listener in JavaScript (placeholder implementation). 46 | /// 47 | public async Task RemoveAsync() 48 | { 49 | await Task.CompletedTask; // Placeholder to maintain async signature. 50 | } 51 | 52 | /// 53 | /// Invokes the callback with arguments from JavaScript. 54 | /// 55 | /// Serialized JSON arguments from JavaScript. 56 | [JSInvokable] 57 | public void Invoke(string args) 58 | { 59 | if (string.IsNullOrWhiteSpace(args) || _argumentType is null) 60 | { 61 | _callbackDelegate.DynamicInvoke(); // Invoke delegate without arguments. 62 | return; 63 | } 64 | 65 | // Deserialize arguments into the expected type. 66 | var deserializedArgs = JsonSerializer.Deserialize(args, _argumentType); 67 | 68 | // Invoke delegate with deserialized arguments. 69 | _callbackDelegate.DynamicInvoke(deserializedArgs); 70 | } 71 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Community.Blazor.MapLibre.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | Community.Blazor.MapLibre 9 | 1.0.1 10 | Yet-Another-Solution 11 | C# Wrapper around a MapLibre GL JS library 12 | https://opensource.org/license/unlicense 13 | https://github.com/Yet-another-solution/Blazor.MapLibre 14 | Blazor; MapLibre; Components 15 | https://maplibre.org/img/maplibre-logos/maplibre-logo-square-dark-blue-bg.png 16 | Community.Blazor.MapLibre 17 | README.md 18 | 19 | 20 | true 21 | 1591 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Converter/OneOfConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Nodes; 3 | using System.Text.Json.Serialization; 4 | using OneOf; 5 | 6 | namespace Community.Blazor.MapLibre.Converter; 7 | 8 | public class OneOfJsonConverter : JsonConverter> 9 | { 10 | public override OneOf Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 11 | { 12 | if (reader.TokenType != JsonTokenType.StartArray) 13 | { 14 | var t1 = JsonSerializer.Deserialize(ref reader, options); 15 | return OneOf.FromT0(t1!); 16 | } 17 | 18 | throw new NotImplementedException("This converter is only intended for serialization."); 19 | } 20 | 21 | public override void Write(Utf8JsonWriter writer, OneOf value, JsonSerializerOptions options) 22 | { 23 | value.Switch( 24 | v1 => JsonSerializer.Serialize(writer, v1, options), 25 | v2 => JsonSerializer.Serialize(writer, v2, options) 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Listener.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre; 2 | 3 | /// 4 | /// Represents a listener that handles the removal of a registered event listener. 5 | /// 6 | /// 7 | /// The Listener class provides a mechanism to manage the lifecycle of event listeners associated with a MapLibre map object. 8 | /// It ensures that resources tied to the listener, such as JavaScript event handlers, are properly disposed when no longer needed. 9 | /// 10 | public class Listener(CallbackHandler action) : IDisposable 11 | { 12 | public void Dispose() 13 | { 14 | _ = Remove(); 15 | } 16 | 17 | public async Task Remove() 18 | { 19 | await action.RemoveAsync(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/MapLibre.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
8 |
-------------------------------------------------------------------------------- /Community.Blazor.MapLibre/MapLibre.razor.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yet-another-solution/Blazor.MapLibre/e3846785432cd92f712aab73cc5b42d52ed37eb9/Community.Blazor.MapLibre/MapLibre.razor.css -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/BulkTransaction.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | /// 6 | /// Represents a bulk transaction to be sent to the MapLibre map. 7 | /// This can be used to send multiple layers and sources to the map in a websocket message. 8 | /// This can be useful to reduce the roundtrip time when adding multiple layers and sources to the map. 9 | /// 10 | public class BulkTransaction 11 | { 12 | public List Transactions { get; } = []; 13 | 14 | public void Add(string eventName, params object?[]? data) 15 | { 16 | Transactions.Add(new Transaction 17 | { 18 | Event = eventName, 19 | Data = data 20 | }); 21 | } 22 | } 23 | 24 | public class Transaction 25 | { 26 | [JsonPropertyName("event")] 27 | public required string Event { get; set; } 28 | 29 | [JsonPropertyName("data")] 30 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 31 | public object?[]? Data { get; set; } 32 | } 33 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/AnimationOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Camera; 4 | 5 | /// 6 | /// Defines options common to map movement methods that involve animation, such as Map#panBy and Map#easeTo. 7 | /// 8 | /// 9 | /// Controls the duration and easing function of the animation. All properties are optional. 10 | /// 11 | public interface IAnimationOptions 12 | { 13 | /// 14 | /// Determines whether animation occurs. 15 | /// 16 | /// 17 | /// If set to false, no animation will occur. 18 | /// 19 | bool? Animate { get; set; } 20 | 21 | /// 22 | /// The animation's duration, measured in milliseconds. 23 | /// 24 | double? Duration { get; set; } 25 | 26 | /// 27 | /// A function that takes a time in the range 0..1 and returns a number where 0 is the initial state and 1 is the final state. 28 | /// 29 | Func? Easing { get; set; } 30 | 31 | /// 32 | /// If true, the animation is considered essential and will not be affected by prefers-reduced-motion. 33 | /// 34 | bool? Essential { get; set; } 35 | 36 | /// 37 | /// Determines whether the camera remains at a constant height based on sea level in 3D maps. 38 | /// 39 | /// 40 | /// Defaults to false. If set to true, the zoom level is recalculated after the animation 41 | /// to maintain the correct distance from the camera to the center-coordinate-altitude. 42 | /// 43 | bool? FreezeElevation { get; set; } 44 | 45 | /// 46 | /// The offset of the target center relative to the real map container center at the end of the animation. 47 | /// 48 | PointLike? Offset { get; set; } 49 | } 50 | 51 | public class AnimationOptions : IAnimationOptions 52 | { 53 | /// 54 | [JsonPropertyName("animate")] 55 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 56 | public bool? Animate { get; set; } 57 | 58 | /// 59 | [JsonPropertyName("duration")] 60 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 61 | public double? Duration { get; set; } 62 | 63 | /// 64 | [JsonPropertyName("easing")] 65 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 66 | public Func? Easing { get; set; } 67 | 68 | /// 69 | [JsonPropertyName("essential")] 70 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 71 | public bool? Essential { get; set; } 72 | 73 | /// 74 | [JsonPropertyName("freezeElevation")] 75 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 76 | public bool? FreezeElevation { get; set; } 77 | 78 | /// 79 | [JsonPropertyName("offset")] 80 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 81 | public PointLike? Offset { get; set; } 82 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/CameraForBoundsOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Community.Blazor.MapLibre.Models.Padding; 3 | using OneOf; 4 | 5 | namespace Community.Blazor.MapLibre.Models.Camera; 6 | 7 | public class CameraForBoundsOptions : CameraOptions 8 | { 9 | /// 10 | /// The maximum zoom level to allow when the camera would transition to the specified bounds. 11 | /// 12 | [JsonPropertyName("maxZoom")] 13 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 14 | public decimal? MaxZoom { get; set; } 15 | /// 16 | /// The center of the given bounds relative to the map's center, measured in pixels. 17 | /// 18 | [JsonPropertyName("offset")] 19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 20 | public PointLike? Offset { get; set; } 21 | /// 22 | /// The amount of padding in pixels to add to the given bounds. 23 | /// 24 | [JsonPropertyName("padding")] 25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 26 | public OneOf Padding { get; set; } 27 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/CameraOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Camera; 4 | 5 | /// 6 | /// Options controlling the desired location, zoom, bearing, pitch, roll, and elevation of the camera. 7 | /// Used with JumpTo, EaseTo, and FlyTo. All properties are optional; 8 | /// if omitted, the current camera values remain unchanged. 9 | /// 10 | public class CameraOptions : ICameraOptions 11 | { 12 | 13 | // 14 | [JsonPropertyName("center")] 15 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 16 | public LngLat? Center { get; set; } 17 | 18 | // 19 | [JsonPropertyName("zoom")] 20 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 21 | public double? Zoom { get; set; } 22 | 23 | // 24 | [JsonPropertyName("bearing")] 25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 26 | public double? Bearing { get; set; } 27 | 28 | // 29 | [JsonPropertyName("elevation")] 30 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 31 | public double? Elevation { get; set; } 32 | 33 | // 34 | [JsonPropertyName("pitch")] 35 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 36 | public double? Pitch { get; set; } 37 | 38 | // 39 | [JsonPropertyName("roll")] 40 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 41 | public double? Roll { get; set; } 42 | } 43 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/CenterZoomBearing.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models.Camera; 2 | 3 | public class CenterZoomBearing : ICenterZoomBearing 4 | { 5 | public LngLat? Center { get; set; } 6 | public double? Zoom { get; set; } 7 | public double? Bearing { get; set; } 8 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/EaseToOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Community.Blazor.MapLibre.Models.Padding; 3 | using OneOf; 4 | 5 | namespace Community.Blazor.MapLibre.Models.Camera; 6 | 7 | public class EaseToOptions : IAnimationOptions, ICameraOptions 8 | { 9 | 10 | /// 11 | /// If `zoom` is specified, `around` determines the point around which the zoom is centered. 12 | /// 13 | [JsonPropertyName("animate")] 14 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 15 | public LngLat? Around { get; set; } 16 | 17 | [JsonPropertyName("delayEndEvents")] 18 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 19 | public double? DelayEndEvents { get; set; } 20 | 21 | [JsonPropertyName("easeId")] 22 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 23 | public string? EaseId { get; set; } 24 | 25 | [JsonPropertyName("noMoveStart")] 26 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 27 | public bool? NoMoveStart { get; set; } 28 | 29 | [JsonPropertyName("padding")] 30 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 31 | public OneOf? Padding { get; set; } 32 | 33 | #region IAnimationOptions 34 | 35 | /// 36 | [JsonPropertyName("animate")] 37 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 38 | public bool? Animate { get; set; } 39 | 40 | /// 41 | [JsonPropertyName("duration")] 42 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 43 | public double? Duration { get; set; } 44 | 45 | /// 46 | [JsonPropertyName("easing")] 47 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 48 | public Func? Easing { get; set; } 49 | 50 | /// 51 | [JsonPropertyName("essential")] 52 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 53 | public bool? Essential { get; set; } 54 | 55 | /// 56 | [JsonPropertyName("freezeElevation")] 57 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 58 | public bool? FreezeElevation { get; set; } 59 | 60 | /// 61 | [JsonPropertyName("offset")] 62 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 63 | public PointLike? Offset { get; set; } 64 | 65 | #endregion 66 | 67 | #region ICameraOptions 68 | 69 | /// 70 | [JsonPropertyName("pitch")] 71 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 72 | public double? Pitch { get; set; } 73 | 74 | /// 75 | [JsonPropertyName("roll")] 76 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 77 | public double? Roll { get; set; } 78 | 79 | /// 80 | [JsonPropertyName("elevation")] 81 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 82 | public double? Elevation { get; set; } 83 | 84 | #region ICenterZoomBearing 85 | 86 | /// 87 | public LngLat? Center { get; set; } 88 | 89 | /// 90 | public double? Zoom { get; set; } 91 | 92 | /// 93 | public double? Bearing { get; set; } 94 | 95 | #endregion 96 | 97 | #endregion 98 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/FitBoundOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Camera; 4 | 5 | public class FitBoundOptions : FlyToOptions 6 | { 7 | /// 8 | /// If `true`, the map transitions using {@link Map#easeTo}. If `false`, the map transitions using {@link Map#flyTo}. 9 | /// See those functions and {@link AnimationOptions} for information about options available. 10 | /// @defaultValue false 11 | /// 12 | [JsonPropertyName("linear")] 13 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 14 | public bool? Linear { get; set; } 15 | 16 | /// 17 | /// The maximum zoom level to allow when the map view transitions to the specified bounds. 18 | /// 19 | [JsonPropertyName("maxZoom")] 20 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 21 | public double? MaxZoom { get; set; } 22 | 23 | /// 24 | /// The center of the given bounds relative to the map's center, measured in pixels. 25 | /// 26 | [JsonPropertyName("offset")] 27 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 28 | public new PointLike? Offset { get; set; } 29 | } 30 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/FlyToOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Camera; 4 | 5 | public class FlyToOptions : IAnimationOptions, ICameraOptions 6 | { 7 | /// 8 | /// The zooming "curve" that will occur along the flight path. 9 | /// 10 | /// 11 | /// A high value maximizes zooming for an exaggerated animation, while a low value minimizes zooming for an effect closer to Map#easeTo. 12 | /// 1.42 is the average value selected in a user study discussed in van Wijk (2003). 13 | /// A value of Math.Pow(6, 0.25) would be equivalent to the root mean squared average velocity. 14 | /// A value of 1 would produce a circular motion. 15 | /// 16 | [JsonPropertyName("curve")] 17 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 18 | public double? Curve { get; set; } 19 | 20 | /// 21 | /// The animation's maximum duration, measured in milliseconds. 22 | /// 23 | /// 24 | /// If the duration exceeds the maximum duration, it resets to 0. 25 | /// 26 | [JsonPropertyName("maxDuration")] 27 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 28 | public double? MaxDuration { get; set; } 29 | 30 | /// 31 | /// The zero-based zoom level at the peak of the flight path. 32 | /// 33 | /// 34 | /// If options.curve is specified, this option is ignored. 35 | /// 36 | [JsonPropertyName("minZoom")] 37 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 38 | public double? MinZoom { get; set; } 39 | 40 | /// 41 | /// The amount of padding in pixels to add to the given bounds. 42 | /// 43 | [JsonPropertyName("padding")] 44 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 45 | public double? Padding { get; set; } 46 | 47 | /// 48 | /// The average speed of the animation measured in screenfuls per second, assuming a linear timing curve. 49 | /// 50 | /// 51 | /// If options.speed is specified, this option is ignored. 52 | /// 53 | [JsonPropertyName("screenSpeed")] 54 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 55 | public double? ScreenSpeed { get; set; } 56 | 57 | /// 58 | /// The average speed of the animation defined in relation to options.curve. 59 | /// 60 | /// 61 | /// A speed of 1.2 means that the map appears to move along the flight path by 1.2 times options.curve screenfuls every second. 62 | /// A screenful is the map's visible span and does not correspond to a fixed physical distance, but varies by zoom level. 63 | /// 64 | [JsonPropertyName("speed")] 65 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 66 | public double? Speed { get; set; } 67 | 68 | #region Interfaces 69 | 70 | /// 71 | [JsonPropertyName("animate")] 72 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 73 | public bool? Animate { get; set; } 74 | 75 | /// 76 | [JsonPropertyName("duration")] 77 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 78 | public double? Duration { get; set; } 79 | 80 | /// 81 | [JsonPropertyName("easing")] 82 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 83 | public Func? Easing { get; set; } 84 | 85 | /// 86 | [JsonPropertyName("essential")] 87 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 88 | public bool? Essential { get; set; } 89 | 90 | /// 91 | [JsonPropertyName("freezeElevation")] 92 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 93 | public bool? FreezeElevation { get; set; } 94 | 95 | /// 96 | [JsonPropertyName("offset")] 97 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 98 | public PointLike? Offset { get; set; } 99 | 100 | /// 101 | [JsonPropertyName("center")] 102 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 103 | public LngLat? Center { get; set; } 104 | 105 | /// 106 | [JsonPropertyName("zoom")] 107 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 108 | public double? Zoom { get; set; } 109 | 110 | /// 111 | [JsonPropertyName("bearing")] 112 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 113 | public double? Bearing { get; set; } 114 | 115 | /// 116 | [JsonPropertyName("pitch")] 117 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 118 | public double? Pitch { get; set; } 119 | 120 | /// 121 | [JsonPropertyName("roll")] 122 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 123 | public double? Roll { get; set; } 124 | 125 | /// 126 | [JsonPropertyName("elevation")] 127 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 128 | public double? Elevation { get; set; } 129 | 130 | #endregion 131 | } 132 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/ICameraOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Camera; 4 | 5 | /// 6 | /// Options common to {@link Map#jumpTo}, {@link Map#easeTo}, and {@link Map#flyTo}, controlling the desired location, 7 | /// zoom, bearing, pitch, and roll of the camera. All properties are optional, and when a property is omitted, the current 8 | /// camera value for that property will remain unchanged. 9 | /// 10 | public interface ICameraOptions : ICenterZoomBearing 11 | { 12 | /// 13 | /// The desired pitch in degrees. The pitch is the angle towards the horizon 14 | /// measured in degrees with a range between 0 and 60 degrees. For example, pitch: 0 provides the appearance 15 | /// of looking straight down at the map, while pitch: 60 tilts the user's perspective towards the horizon. 16 | /// Increasing the pitch value is often used to display 3D objects. 17 | /// 18 | [JsonPropertyName("pitch")] 19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 20 | public double? Pitch { get; set; } 21 | 22 | /// 23 | /// The desired roll in degrees. The roll is the angle about the camera boresight. 24 | /// 25 | [JsonPropertyName("roll")] 26 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 27 | public double? Roll { get; set; } 28 | 29 | /// 30 | /// The elevation of the center point in meters above sea level. 31 | /// 32 | [JsonPropertyName("elevation")] 33 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 34 | public double? Elevation { get; set; } 35 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/ICenterZoomBearing.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models.Camera; 2 | 3 | /// 4 | /// Holds center, zoom and bearing properties 5 | /// 6 | public interface ICenterZoomBearing 7 | { 8 | /// 9 | /// The desired center. 10 | /// 11 | public LngLat? Center { get; set; } 12 | 13 | /// 14 | /// The desired mercator zoom level. 15 | /// 16 | public double? Zoom { get; set; } 17 | 18 | /// 19 | /// The desired bearing in degrees. The bearing is the compass direction that 20 | /// is "up". For example, `bearing: 90` orients the map so that east is up. 21 | /// 22 | public double? Bearing { get; set; } 23 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Camera/JumpToOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Community.Blazor.MapLibre.Models.Padding; 3 | 4 | namespace Community.Blazor.MapLibre.Models.Camera; 5 | 6 | public class JumpToOptions : CameraOptions 7 | { 8 | /// 9 | /// Dimensions in pixels applied on each side of the viewport for shifting the vanishing point. 10 | /// 11 | [JsonPropertyName("padding")] 12 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 13 | public PaddingOptions? Padding { get; set; } 14 | 15 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Control/ControlPosition.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Control; 4 | 5 | /// 6 | /// A position defintion for the control to be placed, can be in one of the corners of the map. When two or more controls are places in the same location they are stacked toward the center of the map. 7 | /// 8 | public enum ControlPosition 9 | { 10 | [JsonStringEnumMemberName("top-left")] 11 | TopLeft, 12 | 13 | [JsonStringEnumMemberName("top-right")] 14 | TopRight, 15 | 16 | [JsonStringEnumMemberName("bottom-left")] 17 | BottomLeft, 18 | 19 | [JsonStringEnumMemberName("bottom-right")] 20 | BottomRight 21 | } 22 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Control/ControlType.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models.Control; 2 | 3 | public enum ControlType 4 | { 5 | AttributionControl, 6 | FullscreenControl, 7 | GeolocateControl, 8 | GlobeControl, 9 | LogoControl, 10 | NavigationControl, 11 | ScaleControl, 12 | TerrainControl 13 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Event/EventType.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Event; 4 | 5 | public enum EventType 6 | { 7 | [JsonStringEnumMemberName("click")] 8 | Click, 9 | 10 | [JsonStringEnumMemberName("contextmenu")] 11 | ContextMenu, 12 | 13 | [JsonStringEnumMemberName("dblclick")] 14 | DblClick, 15 | 16 | [JsonStringEnumMemberName("mousedown")] 17 | MouseDown, 18 | 19 | [JsonStringEnumMemberName("mouseenter")] 20 | MouseEnter, 21 | 22 | [JsonStringEnumMemberName("mouseleave")] 23 | MouseLeave, 24 | 25 | [JsonStringEnumMemberName("mousemove")] 26 | MouseMove, 27 | 28 | [JsonStringEnumMemberName("mouseout")] 29 | MouseOut, 30 | 31 | [JsonStringEnumMemberName("mouseover")] 32 | MouseOver, 33 | 34 | [JsonStringEnumMemberName("mouseup")] 35 | MouseUp 36 | } 37 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Event/MapMouseEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | using Community.Blazor.MapLibre.Models.Feature; 4 | 5 | namespace Community.Blazor.MapLibre.Models.Event; 6 | 7 | public class MapMouseEvent 8 | { 9 | [JsonPropertyName("point")] 10 | public required PointLike Point { get; set; } 11 | 12 | [JsonPropertyName("lngLat")] 13 | public required LngLat LngLat { get; set; } 14 | 15 | [JsonPropertyName("type")] 16 | [JsonConverter(typeof(JsonStringEnumConverter))] 17 | public required EventType Type { get; set; } 18 | 19 | [JsonPropertyName("_defaultPrevented")] 20 | public bool? DefaultPrevented { get; set; } 21 | 22 | /// This is currently a , as we cannot fully deserialize atm. 23 | /// Once layers can be successfully deserialize, this can be changed to . 24 | [JsonPropertyName("features")] 25 | public List Features { get; set; } = []; 26 | } 27 | 28 | public class SimpleFeature 29 | { 30 | [JsonPropertyName("type")] 31 | public required string Type { get; set; } 32 | 33 | [JsonPropertyName("geometry")] 34 | public required IGeometry Geometry { get; set; } 35 | 36 | [JsonPropertyName("properties")] 37 | public Dictionary Properties { get; set; } = []; 38 | } 39 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/FeatureCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class FeatureCollection : IFeature 6 | { 7 | [JsonPropertyName("type")] 8 | public string Type => "FeatureCollection"; 9 | 10 | [JsonPropertyName("features")] 11 | public List Features { get; set; } = []; 12 | 13 | /// 14 | /// 15 | /// 16 | public LngLatBounds GetBounds() 17 | { 18 | if (Features.Count == 0) 19 | { 20 | return new LngLatBounds 21 | { 22 | Southwest = new LngLat(0, 0), 23 | Northeast = new LngLat(0, 0) 24 | }; 25 | } 26 | 27 | var bounds = Features[0].GetBounds(); 28 | for (var i = 1; i < Features.Count; i++) 29 | { 30 | bounds.Extend(Features[i].GetBounds()); 31 | } 32 | 33 | return bounds; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/FeatureFeature.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class FeatureFeature : IFeature 6 | { 7 | [JsonPropertyName("type")] 8 | public string Type => "Feature"; 9 | 10 | [JsonPropertyName("geometry")] 11 | public required IGeometry Geometry { get; set; } 12 | 13 | [JsonPropertyName("properties")] 14 | public Dictionary? Properties { get; set; } 15 | 16 | /// 17 | /// 18 | /// 19 | public LngLatBounds GetBounds() => Geometry.GetBounds(); 20 | } 21 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/FeatureIdentifier.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class FeatureIdentifier 6 | { 7 | /// 8 | /// Unique id of the feature. 9 | /// 10 | [JsonPropertyName("id")] 11 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 12 | public string? Id { get; set; } 13 | 14 | /// 15 | /// The id of the vector or GeoJSON source for the feature. 16 | /// 17 | [JsonPropertyName("source")] 18 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 19 | public string? Source { get; set; } 20 | 21 | /// 22 | /// For vector tile sources, sourceLayer is required. 23 | /// 24 | [JsonPropertyName("sourceLayer")] 25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 26 | public string? SourceLayer { get; set; } 27 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/GeometryType.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | [JsonConverter(typeof(JsonStringEnumConverter))] 6 | public enum GeometryType 7 | { 8 | [JsonStringEnumMemberName("Point")] 9 | Point, 10 | [JsonStringEnumMemberName("MultiPoint")] 11 | MultiPoint, 12 | 13 | [JsonStringEnumMemberName("LineString")] 14 | Line, 15 | [JsonStringEnumMemberName("MultiLineString")] 16 | MultiLine, 17 | 18 | [JsonStringEnumMemberName("Polygon")] 19 | Polygon, 20 | [JsonStringEnumMemberName("MultiPolygon")] 21 | MultiPolygon 22 | } 23 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/IFeature.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | [JsonDerivedType(typeof(FeatureCollection))] 6 | [JsonDerivedType(typeof(FeatureFeature))] 7 | public interface IFeature 8 | { 9 | [JsonPropertyName("type")] 10 | string Type { get; } 11 | 12 | /// 13 | /// Gets the bounding box of the geometry. 14 | /// 15 | /// 16 | /// A object representing the bounding box of the geometry. 17 | /// 18 | LngLatBounds GetBounds(); 19 | } 20 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/IGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] 6 | [JsonDerivedType(typeof(LineGeometry), typeDiscriminator: "LineString")] 7 | [JsonDerivedType(typeof(MultiLineGeometry), typeDiscriminator: "MultiLineString")] 8 | [JsonDerivedType(typeof(MultiPointGeometry), typeDiscriminator: "MultiPoint")] 9 | [JsonDerivedType(typeof(MultiPolygonGeometry), typeDiscriminator: "MultiPolygon")] 10 | [JsonDerivedType(typeof(PointGeometry), typeDiscriminator: "Point")] 11 | [JsonDerivedType(typeof(PolygonGeometry), typeDiscriminator: "Polygon")] 12 | public interface IGeometry 13 | { 14 | [JsonPropertyName("type")] 15 | GeometryType Type { get; } 16 | 17 | /// 18 | /// Gets the bounding box of the geometry. 19 | /// 20 | /// 21 | /// A object representing the bounding box of the geometry. 22 | /// 23 | LngLatBounds GetBounds(); 24 | } 25 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/LineGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class LineGeometry : IGeometry 6 | { 7 | [JsonPropertyName("type")] 8 | public GeometryType Type => GeometryType.Line; 9 | 10 | [JsonPropertyName("coordinates")] 11 | public required double[][] Coordinates { get; set; } 12 | 13 | /// 14 | /// 15 | /// 16 | public LngLatBounds GetBounds() 17 | { 18 | if (Coordinates.Length == 0) 19 | { 20 | return new LngLatBounds 21 | { 22 | Southwest = new LngLat(0, 0), 23 | Northeast = new LngLat(0, 0) 24 | }; 25 | } 26 | 27 | var minLng = double.MaxValue; 28 | var minLat = double.MaxValue; 29 | var maxLng = double.MinValue; 30 | var maxLat = double.MinValue; 31 | 32 | foreach (var coordinates in Coordinates) 33 | { 34 | minLng = Math.Min(minLng, coordinates[0]); 35 | minLat = Math.Min(minLat, coordinates[1]); 36 | maxLng = Math.Max(maxLng, coordinates[0]); 37 | maxLat = Math.Max(maxLat, coordinates[1]); 38 | } 39 | 40 | return new LngLatBounds 41 | { 42 | Southwest = new LngLat(minLng, minLat), 43 | Northeast = new LngLat(maxLng, maxLat) 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/MultiLineGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class MultiLineGeometry : IGeometry 6 | { 7 | [JsonPropertyName("type")] 8 | public GeometryType Type => GeometryType.MultiLine; 9 | 10 | /// 11 | /// 12 | /// 13 | public LngLatBounds GetBounds() 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/MultiPointGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class MultiPointGeometry : IGeometry 6 | { 7 | [JsonPropertyName("type")] 8 | public GeometryType Type => GeometryType.MultiPoint; 9 | 10 | /// 11 | /// 12 | /// 13 | public LngLatBounds GetBounds() 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/MultiPolygonGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class MultiPolygonGeometry : IGeometry 6 | { 7 | [JsonPropertyName("type")] 8 | public GeometryType Type => GeometryType.MultiPolygon; 9 | 10 | /// 11 | /// 12 | /// 13 | public LngLatBounds GetBounds() 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/PointGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class PointGeometry : IGeometry 6 | { 7 | [JsonPropertyName("type")] 8 | public GeometryType Type => GeometryType.Point; 9 | 10 | [JsonPropertyName("coordinates")] 11 | public required double[] Coordinates { get; set; } 12 | 13 | /// 14 | /// 15 | /// 16 | public LngLatBounds GetBounds() 17 | { 18 | return new LngLatBounds 19 | { 20 | Southwest = new LngLat(Coordinates[0], Coordinates[1]), 21 | Northeast = new LngLat(Coordinates[0], Coordinates[1]) 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Feature/PolygonGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Feature; 4 | 5 | public class PolygonGeometry : IGeometry 6 | { 7 | [JsonPropertyName("type")] 8 | public GeometryType Type => GeometryType.Polygon; 9 | 10 | /// 11 | /// Coordinates of a Polygon are an array of LinearRing coordinate arrays. 12 | /// The first element in the array represents the exterior ring. 13 | /// Any subsequent elements represent interior rings (or holes). 14 | /// 15 | /// No holes: 16 | /// 17 | /// { 18 | /// "type": "Polygon", 19 | /// "coordinates": [ 20 | /// [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] 21 | /// ] 22 | /// } 23 | /// 24 | /// 25 | /// 26 | /// With holes: 27 | /// 28 | /// { 29 | /// "type": "Polygon", 30 | /// "coordinates": [ 31 | /// [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ], 32 | /// [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ] 33 | /// ] 34 | /// } 35 | /// 36 | /// 37 | /// 38 | [JsonPropertyName("coordinates")] 39 | public required double[][][] Coordinates { get; set; } 40 | 41 | /// 42 | /// 43 | /// 44 | public LngLatBounds GetBounds() 45 | { 46 | if (Coordinates.Length == 0 || Coordinates[0].Length == 0 || Coordinates[0][0].Length == 0) 47 | { 48 | return new LngLatBounds 49 | { 50 | Southwest = new LngLat(0, 0), 51 | Northeast = new LngLat(0, 0) 52 | }; 53 | } 54 | 55 | var minLng = double.MaxValue; 56 | var minLat = double.MaxValue; 57 | var maxLng = double.MinValue; 58 | var maxLat = double.MinValue; 59 | 60 | foreach (var group in Coordinates) 61 | { 62 | foreach (var coordinate in group) 63 | { 64 | var lng = coordinate[0]; 65 | var lat = coordinate[1]; 66 | 67 | minLng = Math.Min(minLng, lng); 68 | minLat = Math.Min(minLat, lat); 69 | maxLng = Math.Max(maxLng, lng); 70 | maxLat = Math.Max(maxLat, lat); 71 | } 72 | } 73 | 74 | return new LngLatBounds() 75 | { 76 | Northeast = new LngLat(maxLng, maxLat), 77 | Southwest = new LngLat(minLng, minLat) 78 | }; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Image/ImageData.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models.Image; 2 | 3 | public class ImageData 4 | { 5 | public int Width { get; set; } 6 | public int Height { get; set; } 7 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Image/StyleImageMetadata.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Image; 4 | 5 | public class StyleImageMetadata 6 | { 7 | /// 8 | /// Defines the part of the image that can be covered by the content in text-field when icon-text-fit is used. 9 | /// 10 | [JsonPropertyName("content")] 11 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 12 | public double[]? Content { get; set; } 13 | 14 | /// 15 | /// The ratio of pixels in the image to physical pixels on the screen. 16 | /// 17 | [JsonPropertyName("pixelRatio")] 18 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 19 | public double? PixelRatio { get; set; } 20 | 21 | /// 22 | /// Determines whether the image should be interpreted as an SDF (Signed Distance Field) image. 23 | /// 24 | [JsonPropertyName("sdf")] 25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 26 | public bool? Sdf { get; set; } 27 | 28 | /// 29 | /// Defines the part(s) of the image that can be stretched horizontally when icon-text-fit is used. 30 | /// 31 | [JsonPropertyName("stretchX")] 32 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 33 | public double[][]? StretchX { get; set; } 34 | 35 | /// 36 | /// Defines the part(s) of the image that can be stretched vertically when icon-text-fit is used. 37 | /// 38 | [JsonPropertyName("stretchY")] 39 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 40 | public double[][]? StretchY { get; set; } 41 | 42 | /// 43 | /// Defines constraints on the vertical scaling of the image when icon-text-fit is used. 44 | /// 45 | [JsonPropertyName("textFitHeight")] 46 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 47 | public TextFit? TextFitHeight { get; set; } 48 | 49 | /// 50 | /// Defines constraints on the horizontal scaling of the image when icon-text-fit is used. 51 | /// 52 | [JsonPropertyName("textFitWidth")] 53 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 54 | public TextFit? TextFitWidth { get; set; } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/BackgroundLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Layers; 4 | 5 | public class BackgroundLayer : Layer 6 | { 7 | /// 8 | /// 9 | /// 10 | [JsonPropertyName("type")] 11 | [JsonConverter(typeof(JsonStringEnumConverter))] 12 | public override LayerType Type => LayerType.Background; 13 | } 14 | 15 | public class BackgroundLayerLayout; 16 | public class BackgroundLayerPaint; 17 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/CircleLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | using OneOf; 3 | using System.Text.Json.Serialization; 4 | using Community.Blazor.MapLibre.Converter; 5 | 6 | namespace Community.Blazor.MapLibre.Models.Layers; 7 | 8 | public class CircleLayer : Layer 9 | { 10 | /// 11 | /// 12 | /// 13 | [JsonPropertyName("type")] 14 | [JsonConverter(typeof(JsonStringEnumConverter))] 15 | public override LayerType Type => LayerType.Circle; 16 | 17 | /// 18 | /// Gets or sets the name of the source to be used for this layer. 19 | /// 20 | [JsonPropertyName("source")] 21 | public required string Source { get; set; } 22 | 23 | [JsonPropertyName("circle-sort-key")] 24 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 25 | public double? CircleSortKey { get; set; } 26 | 27 | [JsonPropertyName("visibility")] 28 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 29 | public Visibility? Visibility { get; set; } 30 | } 31 | 32 | public class CircleLayerLayout; 33 | 34 | public class CircleLayerPaint 35 | { 36 | [JsonPropertyName("circle-radius")] 37 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 38 | [JsonConverter(typeof(OneOfJsonConverter))] 39 | public OneOf? CircleRadius { get; set; } 40 | 41 | [JsonPropertyName("circle-color")] 42 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 43 | [JsonConverter(typeof(OneOfJsonConverter))] 44 | public OneOf? CircleColor { get; set; } 45 | 46 | [JsonPropertyName("circle-blur")] 47 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 48 | [JsonConverter(typeof(OneOfJsonConverter))] 49 | public OneOf? CircleBlur { get; set; } 50 | 51 | [JsonPropertyName("circle-opacity")] 52 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 53 | [JsonConverter(typeof(OneOfJsonConverter))] 54 | public OneOf? CircleOpacity { get; set; } 55 | 56 | [JsonPropertyName("circle-translate")] 57 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 58 | [JsonConverter(typeof(OneOfJsonConverter))] 59 | public OneOf? CircleTranslate { get; set; } 60 | 61 | [JsonPropertyName("circle-translate-anchor")] 62 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 63 | [JsonConverter(typeof(OneOfJsonConverter))] 64 | public OneOf? CircleTranslateAnchor { get; set; } 65 | 66 | [JsonPropertyName("circle-pitch-scale")] 67 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 68 | [JsonConverter(typeof(OneOfJsonConverter))] 69 | public OneOf? CirclePitchScale { get; set; } 70 | 71 | [JsonPropertyName("circle-pitch-alignment")] 72 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 73 | [JsonConverter(typeof(OneOfJsonConverter))] 74 | public OneOf? CirclePitchAlignment { get; set; } 75 | 76 | [JsonPropertyName("circle-stroke-width")] 77 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 78 | [JsonConverter(typeof(OneOfJsonConverter))] 79 | public OneOf? CircleStrokeWidth { get; set; } 80 | 81 | [JsonPropertyName("circle-stroke-color")] 82 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 83 | [JsonConverter(typeof(OneOfJsonConverter))] 84 | public OneOf? CircleStrokeColor { get; set; } 85 | 86 | [JsonPropertyName("circle-stroke-opacity")] 87 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 88 | [JsonConverter(typeof(OneOfJsonConverter))] 89 | public OneOf? CircleStrokeOpacity { get; set; } 90 | } 91 | 92 | [JsonConverter(typeof(JsonStringEnumConverter))] 93 | public enum MapViewport 94 | { 95 | [JsonStringEnumMemberName("map")] 96 | Map, 97 | 98 | [JsonStringEnumMemberName("viewport")] 99 | Viewport 100 | } 101 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/FillExtrusionLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Layers; 4 | 5 | public class FillExtrusionLayer : Layer 6 | { 7 | /// 8 | /// 9 | /// 10 | [JsonPropertyName("type")] 11 | [JsonConverter(typeof(JsonStringEnumConverter))] 12 | public override LayerType Type => LayerType.FillExtrusion; 13 | 14 | /// 15 | /// Gets or sets the name of the source to be used for this layer. 16 | /// 17 | [JsonPropertyName("source")] 18 | public required string Source { get; set; } 19 | } 20 | 21 | public class FillExtrusionLayerLayout; 22 | public class FillExtrusionLayerPaint; 23 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/FillLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | using System.Text.Json.Serialization; 3 | using Community.Blazor.MapLibre.Converter; 4 | using OneOf; 5 | 6 | namespace Community.Blazor.MapLibre.Models.Layers; 7 | 8 | public class FillLayer : Layer 9 | { 10 | /// 11 | /// 12 | /// 13 | [JsonPropertyName("type")] 14 | [JsonConverter(typeof(JsonStringEnumConverter))] 15 | public override LayerType Type => LayerType.Fill; 16 | 17 | /// 18 | /// Gets or sets the name of the source to be used for this layer. 19 | /// 20 | [JsonPropertyName("source")] 21 | public required string Source { get; set; } 22 | } 23 | 24 | public class FillLayerLayout 25 | { 26 | /// 27 | /// Determines the rendering order of features based on their sort key. 28 | /// 29 | /// 30 | /// Features are sorted in ascending order based on this value. Features with a higher sort key will appear above features with a lower sort key. 31 | /// 32 | /// 33 | /// 34 | /// Sort Key 35 | /// Rendering Order 36 | /// 37 | /// 38 | /// Lower value 39 | /// Feature is drawn first and appears beneath features with a higher sort key. 40 | /// 41 | /// 42 | /// Higher value 43 | /// Feature is drawn later and appears above features with a lower sort key. 44 | /// 45 | /// 46 | /// 47 | [JsonPropertyName("fill-sort-key")] 48 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 49 | [JsonConverter(typeof(OneOfJsonConverter))] 50 | public OneOf? FillSortKey { get; set; } 51 | 52 | /// 53 | /// Controls whether this layer is displayed. 54 | /// 55 | /// 56 | /// Optional enum. Possible values: visible, none. Defaults to visible. 57 | /// 58 | /// 59 | /// 60 | /// Value 61 | /// Effect 62 | /// 63 | /// 64 | /// visible 65 | /// The layer is shown. 66 | /// 67 | /// 68 | /// none 69 | /// The layer is not shown. 70 | /// 71 | /// 72 | /// 73 | [JsonPropertyName("visibility")] 74 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 75 | [JsonConverter(typeof(OneOfJsonConverter))] 76 | public OneOf? Visibility { get; set; } 77 | } 78 | 79 | public class FillLayerPaint 80 | { 81 | /// 82 | /// Determines whether the fill should be antialiased. 83 | /// 84 | /// 85 | /// Optional boolean. Defaults to true. 86 | /// 87 | /// 88 | /// 89 | /// Value 90 | /// Effect 91 | /// 92 | /// 93 | /// true 94 | /// The fill is antialiased, resulting in smoother edges. 95 | /// 96 | /// 97 | /// false 98 | /// The fill is not antialiased, which may result in jagged edges. 99 | /// 100 | /// 101 | /// 102 | [JsonPropertyName("fill-antialias")] 103 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 104 | [JsonConverter(typeof(OneOfJsonConverter))] 105 | public OneOf? FillAntialias { get; set; } 106 | 107 | /// 108 | /// Specifies the opacity of the entire fill layer. 109 | /// 110 | /// 111 | /// Optional number in the range [0, 1]. Defaults to 1. Supports feature-state and interpolate expressions. Transitionable. 112 | /// 113 | /// The opacity setting affects both the fill color and the 1px stroke around the fill if the stroke is used. 114 | /// 115 | /// 116 | /// 117 | /// Value 118 | /// Effect 119 | /// 120 | /// 121 | /// 0 122 | /// The fill layer is fully transparent. 123 | /// 124 | /// 125 | /// 0.5 126 | /// The fill layer is 50% transparent. 127 | /// 128 | /// 129 | /// 1 130 | /// The fill layer is fully opaque. 131 | /// 132 | /// 133 | /// Uses interpolate expressions 134 | /// Allows dynamic adjustments to the fill opacity. 135 | /// 136 | /// 137 | /// 138 | [JsonPropertyName("fill-opacity")] 139 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 140 | [JsonConverter(typeof(OneOfJsonConverter))] 141 | public OneOf? FillOpacity { get; set; } 142 | 143 | /// 144 | /// Defines the color of the filled part of this layer. 145 | /// 146 | /// 147 | /// Optional color. Defaults to #000000. Disabled by fill-pattern. Supports feature-state and interpolate expressions. Transitionable. 148 | /// 149 | /// This color can be specified as rgba with an alpha component, and the color's opacity will not affect the opacity of the 1px stroke if it is used. 150 | /// 151 | /// 152 | /// 153 | /// Value 154 | /// Effect 155 | /// 156 | /// 157 | /// #000000 (default) 158 | /// The fill color is black. 159 | /// 160 | /// 161 | /// rgba(r, g, b, a) 162 | /// Specifies a custom color with an optional alpha component for transparency. 163 | /// 164 | /// 165 | /// Uses interpolate expressions 166 | /// Allows dynamic adjustments to the fill color. 167 | /// 168 | /// 169 | /// fill-pattern is set 170 | /// The fill color is disabled in favor of a pattern. 171 | /// 172 | /// 173 | /// 174 | [JsonPropertyName("fill-color")] 175 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 176 | [JsonConverter(typeof(OneOfJsonConverter))] 177 | public OneOf? FillColor { get; set; } 178 | 179 | /// 180 | /// Specifies the outline color of the fill. 181 | /// 182 | /// 183 | /// Optional color. Disabled by fill-pattern. Requires fill-antialias to be true. Supports feature-state and interpolate expressions. Transitionable. 184 | /// 185 | /// If unspecified, the outline color matches the value of fill-color. 186 | /// 187 | /// 188 | /// 189 | /// Condition 190 | /// Effect 191 | /// 192 | /// 193 | /// Value not set 194 | /// Defaults to the value of fill-color. 195 | /// 196 | /// 197 | /// Custom color set 198 | /// Uses the specified color for the outline. 199 | /// 200 | /// 201 | /// fill-pattern is set 202 | /// The outline color is disabled. 203 | /// 204 | /// 205 | /// fill-antialias is false 206 | /// The outline color is not applied. 207 | /// 208 | /// 209 | /// Uses interpolate expressions 210 | /// Allows dynamic adjustments to the outline color. 211 | /// 212 | /// 213 | /// 214 | [JsonPropertyName("fill-outline-color")] 215 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 216 | [JsonConverter(typeof(OneOfJsonConverter))] 217 | public OneOf? FillOutlineColor { get; set; } 218 | 219 | /// 220 | /// Specifies the geometry's offset in pixels. 221 | /// 222 | /// 223 | /// Optional array. Units are in pixels. Defaults to [0, 0]. Supports interpolate expressions. Transitionable. 224 | /// 225 | /// Values are specified as [x, y], where negative values indicate left and up, respectively. 226 | /// 227 | /// 228 | /// 229 | /// Value 230 | /// Effect 231 | /// 232 | /// 233 | /// [0, 0] (default) 234 | /// No offset is applied. 235 | /// 236 | /// 237 | /// [x, y] with positive x 238 | /// Moves the geometry to the right. 239 | /// 240 | /// 241 | /// [x, y] with negative x 242 | /// Moves the geometry to the left. 243 | /// 244 | /// 245 | /// [x, y] with positive y 246 | /// Moves the geometry downward. 247 | /// 248 | /// 249 | /// [x, y] with negative y 250 | /// Moves the geometry upward. 251 | /// 252 | /// 253 | /// Uses interpolate expressions 254 | /// Allows dynamic adjustments to the offset values. 255 | /// 256 | /// 257 | /// 258 | [JsonPropertyName("fill-translate")] 259 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 260 | [JsonConverter(typeof(OneOfJsonConverter))] 261 | public OneOf? FillTranslate { get; set; } 262 | 263 | /// 264 | /// Controls the frame of reference for fill-translate. 265 | /// 266 | /// 267 | /// Optional enum. Possible values: map, viewport. Defaults to map. Requires fill-translate. 268 | /// 269 | /// 270 | /// 271 | /// Value 272 | /// Effect 273 | /// 274 | /// 275 | /// map (default) 276 | /// The fill is translated relative to the map. 277 | /// 278 | /// 279 | /// viewport 280 | /// The fill is translated relative to the viewport. 281 | /// 282 | /// 283 | /// 284 | [JsonPropertyName("fill-translate-anchor")] 285 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 286 | [JsonConverter(typeof(OneOfJsonConverter))] 287 | public OneOf? FillTranslateAnchor { get; set; } 288 | 289 | /// 290 | /// Specifies the name of the image in the sprite to use for drawing image fills. 291 | /// 292 | /// 293 | /// Optional resolvedImage. Transitionable. 294 | /// 295 | /// For seamless patterns, the image width and height must be a power of two (e.g., 2, 4, 8, ..., 512). 296 | /// Note that zoom-dependent expressions will be evaluated only at integer zoom levels. 297 | /// 298 | /// 299 | /// 300 | /// Condition 301 | /// Effect 302 | /// 303 | /// 304 | /// Image width and height are a power of two 305 | /// Ensures seamless pattern rendering. 306 | /// 307 | /// 308 | /// Zoom-dependent expressions 309 | /// Evaluated only at integer zoom levels. 310 | /// 311 | /// 312 | /// 313 | [JsonPropertyName("fill-pattern")] 314 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 315 | [JsonConverter(typeof(OneOfJsonConverter))] 316 | public OneOf? FillPattern { get; set; } 317 | } 318 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/HeatMapLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Layers; 4 | 5 | public class HeatMapLayer : Layer 6 | { 7 | /// 8 | /// 9 | /// 10 | [JsonPropertyName("type")] 11 | [JsonConverter(typeof(JsonStringEnumConverter))] 12 | public override LayerType Type => LayerType.Heatmap; 13 | 14 | /// 15 | /// Gets or sets the name of the source to be used for this layer. 16 | /// 17 | [JsonPropertyName("source")] 18 | public required string Source { get; set; } 19 | } 20 | 21 | public class HeatMapLayerLayout; 22 | public class HeatMapLayerPaint; 23 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/HillShadeLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Layers; 4 | 5 | public class HillShadeLayer : Layer 6 | { 7 | /// 8 | /// 9 | /// 10 | [JsonPropertyName("type")] 11 | [JsonConverter(typeof(JsonStringEnumConverter))] 12 | public override LayerType Type => LayerType.Hillshade; 13 | 14 | /// 15 | /// Gets or sets the name of the source to be used for this layer. 16 | /// 17 | [JsonPropertyName("source")] 18 | public required string Source { get; set; } 19 | } 20 | 21 | public class HillShadeLayerLayout; 22 | public class HillShadeLayerPaint; 23 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/Layer.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Community.Blazor.MapLibre.Models.Layers; 5 | 6 | /// 7 | /// Represents a Layer in the MapLibre map which defines rendering and customization of different map elements. 8 | /// 9 | [JsonDerivedType(typeof(BackgroundLayer))] 10 | [JsonDerivedType(typeof(CircleLayer))] 11 | [JsonDerivedType(typeof(FillExtrusionLayer))] 12 | [JsonDerivedType(typeof(FillLayer))] 13 | [JsonDerivedType(typeof(HeatMapLayer))] 14 | [JsonDerivedType(typeof(HillShadeLayer))] 15 | [JsonDerivedType(typeof(LineLayer))] 16 | [JsonDerivedType(typeof(RasterLayer))] 17 | [JsonDerivedType(typeof(SymbolLayer))] 18 | public abstract class Layer 19 | { 20 | /// 21 | /// Gets or sets the unique name of the layer. This is required. 22 | /// 23 | [JsonPropertyName("id")] 24 | public required string Id { get; set; } 25 | 26 | /// 27 | /// Gets or sets the rendering type of the layer. This is required. 28 | /// Possible values: "fill", "line", "symbol", "circle", "heatmap", "fill-extrusion", "raster", "hillshade", "background". 29 | /// 30 | [JsonPropertyName("type")] 31 | [JsonConverter(typeof(JsonStringEnumConverter))] 32 | public abstract LayerType Type { get; } 33 | 34 | /// 35 | /// The minimum zoom level for the layer. At zoom levels less than the minzoom, the layer will be hidden. 36 | /// 37 | [JsonPropertyName("minzoom")] 38 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 39 | public float? MinZoom { get; set; } 40 | 41 | /// 42 | /// The maximum zoom level for the layer. At zoom levels equal to or greater than the maxzoom, the layer will be hidden. 43 | /// 44 | [JsonPropertyName("maxzoom")] 45 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 46 | public float? MaxZoom { get; set; } 47 | 48 | [JsonPropertyName("filter")] 49 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 50 | public object? Filter { get; set; } // ["==", ["get", "color" ], "polygon"] 51 | } 52 | 53 | public abstract class Layer : Layer 54 | { 55 | /// 56 | /// Gets or sets the layout properties for the layer. 57 | /// Optional. Layout defines how the layer features are placed on the map. 58 | /// 59 | [JsonPropertyName("layout")] 60 | [StringSyntax(StringSyntaxAttribute.Json)] 61 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 62 | public TLayout? Layout { get; set; } 63 | 64 | /// 65 | /// Gets or sets the paint properties for the layer. 66 | /// Optional. Paint defines the visual styling of the features. 67 | /// 68 | [JsonPropertyName("paint")] 69 | [StringSyntax(StringSyntaxAttribute.Json)] 70 | public TPaint? Paint { get; set; } 71 | } 72 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/LayerType.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Layers; 4 | 5 | public enum LayerType 6 | { 7 | [JsonStringEnumMemberName("fill")] 8 | Fill, 9 | 10 | [JsonStringEnumMemberName("line")] 11 | Line, 12 | 13 | [JsonStringEnumMemberName("symbol")] 14 | Symbol, 15 | 16 | [JsonStringEnumMemberName("circle")] 17 | Circle, 18 | 19 | [JsonStringEnumMemberName("heatmap")] 20 | Heatmap, 21 | 22 | [JsonStringEnumMemberName("fill-extrusion")] 23 | FillExtrusion, 24 | 25 | [JsonStringEnumMemberName("raster")] 26 | Raster, 27 | 28 | [JsonStringEnumMemberName("hillshade")] 29 | Hillshade, 30 | 31 | [JsonStringEnumMemberName("background")] 32 | Background 33 | } 34 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/LineLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | using System.Text.Json.Serialization; 3 | using Community.Blazor.MapLibre.Converter; 4 | using OneOf; 5 | 6 | namespace Community.Blazor.MapLibre.Models.Layers; 7 | 8 | public class LineLayer : Layer 9 | { 10 | /// 11 | /// 12 | /// 13 | [JsonPropertyName("type")] 14 | [JsonConverter(typeof(JsonStringEnumConverter))] 15 | public override LayerType Type => LayerType.Line; 16 | 17 | /// 18 | /// Gets or sets the name of the source to be used for this layer. 19 | /// 20 | [JsonPropertyName("source")] 21 | public required string Source { get; set; } 22 | } 23 | 24 | public class LineLayerLayout 25 | { 26 | /// 27 | /// The display of line endings. 28 | /// Possible values: butt, round, square. Defaults to butt. 29 | /// 30 | [JsonPropertyName("line-cap")] 31 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 32 | [JsonConverter(typeof(OneOfJsonConverter))] 33 | public OneOf? LineCap { get; set; } 34 | 35 | /// 36 | /// The display of lines when joining. 37 | /// Possible values: bevel, round, miter. Defaults to miter. 38 | /// 39 | [JsonPropertyName("line-join")] 40 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 41 | [JsonConverter(typeof(OneOfJsonConverter))] 42 | public OneOf? LineJoin { get; set; } 43 | 44 | /// 45 | /// Used to automatically convert miter joins to bevel joins for sharp angles. 46 | /// Defaults to 2. 47 | /// 48 | [JsonPropertyName("line-miter-limit")] 49 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 50 | [JsonConverter(typeof(OneOfJsonConverter))] 51 | public OneOf? LineMiterLimit { get; set; } 52 | 53 | /// 54 | /// Used to automatically convert round joins to miter joins for shallow angles. 55 | /// Defaults to 1.05. 56 | /// 57 | [JsonPropertyName("line-round-limit")] 58 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 59 | [JsonConverter(typeof(OneOfJsonConverter))] 60 | public OneOf? LineRoundLimit { get; set; } 61 | 62 | /// 63 | /// Sorts features in ascending order based on this value. 64 | /// 65 | [JsonPropertyName("line-sort-key")] 66 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 67 | [JsonConverter(typeof(OneOfJsonConverter))] 68 | public OneOf? LineSortKey { get; set; } 69 | 70 | /// 71 | /// Whether this layer is displayed. 72 | /// Possible values: visible, none. Defaults to visible. 73 | /// 74 | [JsonPropertyName("visibility")] 75 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 76 | [JsonConverter(typeof(OneOfJsonConverter))] 77 | public OneOf? Visibility { get; set; } 78 | } 79 | 80 | public class LineLayerPaint 81 | { 82 | /// 83 | /// The opacity at which the line will be drawn. Defaults to 1. 84 | /// Range: [0, 1]. 85 | /// 86 | [JsonPropertyName("line-opacity")] 87 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 88 | [JsonConverter(typeof(OneOfJsonConverter))] 89 | public OneOf? LineOpacity { get; set; } 90 | 91 | /// 92 | /// The color with which the line will be drawn. Defaults to #000000. 93 | /// 94 | [JsonPropertyName("line-color")] 95 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 96 | [JsonConverter(typeof(OneOfJsonConverter))] 97 | public OneOf? LineColor { get; set; } 98 | 99 | /// 100 | /// The geometry's offset. Values are [x, y] in pixels. 101 | /// 102 | [JsonPropertyName("line-translate")] 103 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 104 | [JsonConverter(typeof(OneOfJsonConverter))] 105 | public OneOf? LineTranslate { get; set; } 106 | 107 | /// 108 | /// Controls the frame of reference for line-translate. 109 | /// Possible values: map, viewport. Defaults to map. 110 | /// 111 | [JsonPropertyName("line-translate-anchor")] 112 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 113 | [JsonConverter(typeof(OneOfJsonConverter))] 114 | public OneOf? LineTranslateAnchor { get; set; } 115 | 116 | /// 117 | /// Stroke thickness. Defaults to 1. 118 | /// 119 | [JsonPropertyName("line-width")] 120 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 121 | [JsonConverter(typeof(OneOfJsonConverter))] 122 | public OneOf? LineWidth { get; set; } 123 | 124 | /// 125 | /// Draws a line casing outside of a line's actual path. Defaults to 0. 126 | /// 127 | [JsonPropertyName("line-gap-width")] 128 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 129 | [JsonConverter(typeof(OneOfJsonConverter))] 130 | public OneOf? LineGapWidth { get; set; } 131 | 132 | /// 133 | /// The line's offset. Defaults to 0. 134 | /// 135 | [JsonPropertyName("line-offset")] 136 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 137 | [JsonConverter(typeof(OneOfJsonConverter))] 138 | public OneOf? LineOffset { get; set; } 139 | 140 | /// 141 | /// Blur applied to the line, in pixels. Defaults to 0. 142 | /// 143 | [JsonPropertyName("line-blur")] 144 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 145 | [JsonConverter(typeof(OneOfJsonConverter))] 146 | public OneOf? LineBlur { get; set; } 147 | 148 | /// 149 | /// Specifies the lengths of the alternating dashes and gaps that form the dash pattern. 150 | /// 151 | [JsonPropertyName("line-dasharray")] 152 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 153 | [JsonConverter(typeof(OneOfJsonConverter))] 154 | public OneOf? LineDasharray { get; set; } 155 | 156 | /// 157 | /// Name of image in sprite to use for drawing image lines. 158 | /// 159 | [JsonPropertyName("line-pattern")] 160 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 161 | [JsonConverter(typeof(OneOfJsonConverter))] 162 | public OneOf? LinePattern { get; set; } 163 | 164 | /// 165 | /// Defines a gradient with which to color a line feature. 166 | /// 167 | [JsonPropertyName("line-gradient")] 168 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 169 | [JsonConverter(typeof(OneOfJsonConverter))] 170 | public OneOf? LineGradient { get; set; } 171 | } 172 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Layers/RasterLayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Layers; 4 | 5 | public class RasterLayer : Layer 6 | { 7 | /// 8 | /// 9 | /// 10 | [JsonPropertyName("type")] 11 | [JsonConverter(typeof(JsonStringEnumConverter))] 12 | public override LayerType Type => LayerType.Raster; 13 | 14 | /// 15 | /// Gets or sets the name of the source to be used for this layer. 16 | /// 17 | [JsonPropertyName("source")] 18 | public required string Source { get; set; } 19 | } 20 | 21 | public class RasterLayerLayout; 22 | public class RasterLayerPaint; 23 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/LngLat.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | /// 6 | /// Represents a geographical coordinate (longitude and latitude). 7 | /// 8 | public class LngLat 9 | { 10 | [JsonPropertyName("lng")] 11 | public double Longitude { get; set; } 12 | 13 | [JsonPropertyName("lat")] 14 | public double Latitude { get; set; } 15 | 16 | /// 17 | /// Default constructor. Allows initialization with object initializers. 18 | /// 19 | public LngLat() 20 | { 21 | Longitude = 0; 22 | Latitude = 0; 23 | } 24 | 25 | /// 26 | /// Constructor that initializes the coordinate with provided longitude and latitude values. 27 | /// 28 | /// The longitude of the coordinate. 29 | /// The latitude of the coordinate. 30 | public LngLat(double longitude, double latitude) 31 | { 32 | Longitude = longitude; 33 | Latitude = latitude; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/LngLatBounds.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | /// 6 | /// Represents a bounding box for geographical coordinates. It allows operations such as 7 | /// extending the bounds, checking points within bounds, and adjusting bounds that cross the anti-meridian. 8 | /// 9 | public class LngLatBounds 10 | { 11 | /// 12 | /// The southwest corner of the bounding box. 13 | /// 14 | [JsonPropertyName("_sw")] 15 | public required LngLat Southwest { get; set; } 16 | 17 | /// 18 | /// The northeast corner of the bounding box. 19 | /// 20 | [JsonPropertyName("_ne")] 21 | public required LngLat Northeast { get; set; } 22 | 23 | /// 24 | /// Extends the bounds to include the specified point. 25 | /// 26 | /// The point to include. 27 | public void Extend(LngLat point) 28 | { 29 | Southwest.Longitude = Math.Min(Southwest.Longitude, point.Longitude); 30 | Southwest.Latitude = Math.Min(Southwest.Latitude, point.Latitude); 31 | Northeast.Longitude = Math.Max(Northeast.Longitude, point.Longitude); 32 | Northeast.Latitude = Math.Max(Northeast.Latitude, point.Latitude); 33 | } 34 | 35 | /// 36 | /// Extends the bounds to include another bounding box. 37 | /// 38 | /// The bounds to include. 39 | public void Extend(LngLatBounds bounds) 40 | { 41 | Extend(bounds.Southwest); 42 | Extend(bounds.Northeast); 43 | } 44 | 45 | /// 46 | /// Returns the center point of the bounding box. 47 | /// 48 | /// A representing the center of the bounding box. 49 | public LngLat GetCenter() 50 | { 51 | return new LngLat( 52 | (Southwest.Longitude + Northeast.Longitude) / 2, 53 | (Southwest.Latitude + Northeast.Latitude) / 2 54 | ); 55 | } 56 | 57 | /// 58 | /// Returns whether the provided point lies within the bounds. 59 | /// 60 | /// The point to check. 61 | /// True if the point is within bounds, otherwise false. 62 | public bool Contains(LngLat point) 63 | { 64 | var containsLatitude = Southwest.Latitude <= point.Latitude && point.Latitude <= Northeast.Latitude; 65 | var containsLongitude = Southwest.Longitude <= point.Longitude && point.Longitude <= Northeast.Longitude; 66 | 67 | // Handle anti-meridian crossing 68 | if (Southwest.Longitude > Northeast.Longitude) 69 | { 70 | containsLongitude = Southwest.Longitude <= point.Longitude || point.Longitude <= Northeast.Longitude; 71 | } 72 | 73 | return containsLatitude && containsLongitude; 74 | } 75 | 76 | /// 77 | /// Converts the bounds to an array in the format of [[swLng, swLat], [neLng, neLat]]. 78 | /// 79 | /// A two-dimensional array representing the bounds. 80 | public double[][] ToArray() 81 | { 82 | return new[] 83 | { 84 | new[] { Southwest.Longitude, Southwest.Latitude }, 85 | new[] { Northeast.Longitude, Northeast.Latitude } 86 | }; 87 | } 88 | 89 | /// 90 | /// Converts the bounds to a string. 91 | /// 92 | /// A string representation of the bounds. 93 | public override string ToString() 94 | { 95 | return 96 | $"LngLatBounds([{Southwest.Longitude}, {Southwest.Latitude}], [{Northeast.Longitude}, {Northeast.Latitude}])"; 97 | } 98 | 99 | /// 100 | /// Adjusts bounds that cross the anti-meridian. 101 | /// 102 | /// An adjusted if crossing occurred, otherwise the original bounds. 103 | public LngLatBounds AdjustAntiMeridian() 104 | { 105 | if (Southwest.Longitude > Northeast.Longitude) 106 | { 107 | return new LngLatBounds 108 | { 109 | Southwest = Southwest, 110 | Northeast = new LngLat(Northeast.Longitude + 360, Northeast.Latitude) 111 | }; 112 | } 113 | 114 | return this; 115 | } 116 | 117 | /// 118 | /// Creates a new bounds object based on a center point and radius. 119 | /// 120 | /// The center of the bounding box. 121 | /// The radius in meters extending from the center. 122 | /// A new object. 123 | public static LngLatBounds FromLngLat(LngLat center, double radius) 124 | { 125 | const double earthCircumference = 40075017; // in meters at the equator 126 | var latAccuracy = 360 * radius / earthCircumference; 127 | var lngAccuracy = latAccuracy / Math.Cos(center.Latitude * Math.PI / 180); 128 | 129 | var southwest = new LngLat(center.Longitude - lngAccuracy, center.Latitude - latAccuracy); 130 | var northeast = new LngLat(center.Longitude + lngAccuracy, center.Latitude + latAccuracy); 131 | return new LngLatBounds { Southwest = southwest, Northeast = northeast }; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Marker/Marker.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models.Marker; 2 | 3 | public class Marker 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Marker/MarkerAlignment.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Community.Blazor.MapLibre.Models.Marker; 5 | 6 | /// 7 | /// Enumeration for marker alignment options. 8 | /// 9 | [JsonConverter(typeof(JsonStringEnumConverter))] 10 | public enum MarkerAlignment 11 | { 12 | [EnumMember(Value = "auto")] 13 | Auto, 14 | 15 | [EnumMember(Value = "map")] 16 | Map, 17 | 18 | [EnumMember(Value = "viewport")] 19 | Viewport 20 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Marker/MarkerAnchor.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Community.Blazor.MapLibre.Models.Marker; 5 | 6 | /// 7 | /// Enumeration for marker anchor positions. 8 | /// 9 | [JsonConverter(typeof(JsonStringEnumConverter))] 10 | public enum MarkerAnchor 11 | { 12 | [EnumMember(Value = "center")] 13 | Center, 14 | 15 | [EnumMember(Value = "top")] 16 | Top, 17 | 18 | [EnumMember(Value = "bottom")] 19 | Bottom, 20 | 21 | [EnumMember(Value = "left")] 22 | Left, 23 | 24 | [EnumMember(Value = "right")] 25 | Right, 26 | 27 | [EnumMember(Value = "top-left")] 28 | TopLeft, 29 | 30 | [EnumMember(Value = "top-right")] 31 | TopRight, 32 | 33 | [EnumMember(Value = "bottom-left")] 34 | BottomLeft, 35 | 36 | [EnumMember(Value = "bottom-right")] 37 | BottomRight 38 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Marker/MarkerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models.Marker; 2 | 3 | using System.Text.Json.Serialization; 4 | using Microsoft.AspNetCore.Components; 5 | 6 | /// 7 | /// Options for configuring a marker on the map. 8 | /// 9 | public class MarkerOptions 10 | { 11 | /// 12 | /// A string indicating the part of the Marker that should be positioned closest to the coordinate. 13 | /// Options are 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', and 'bottom-right'. 14 | /// 15 | [JsonPropertyName("anchor")] 16 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 17 | public MarkerAnchor? Anchor { get; set; } 18 | 19 | /// 20 | /// Space-separated CSS class names to add to marker element. 21 | /// 22 | [JsonPropertyName("className")] 23 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 24 | public string? ClassName { get; set; } 25 | 26 | /// 27 | /// The max number of pixels a user can shift the mouse pointer during a click on the marker for it to be 28 | /// considered a valid click (as opposed to a marker drag). The default is to inherit map's clickTolerance. 29 | /// 30 | [JsonPropertyName("clickTolerance")] 31 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 32 | public double? ClickTolerance { get; set; } 33 | 34 | /// 35 | /// The color to use for the default marker if options.element is not provided. The default is light blue. 36 | /// 37 | [JsonPropertyName("color")] 38 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 39 | public string? Color { get; set; } 40 | 41 | /// 42 | /// A boolean indicating whether or not a marker is able to be dragged to a new position on the map. 43 | /// 44 | [JsonPropertyName("draggable")] 45 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 46 | public bool? Draggable { get; set; } 47 | 48 | /// 49 | /// DOM element to use as a marker. The default is a light blue, droplet-shaped SVG marker. 50 | /// 51 | [JsonIgnore] 52 | public ElementReference? Element { get; set; } 53 | 54 | /// 55 | /// The offset in pixels as a [x, y] array to apply relative to the element's center. 56 | /// Negatives indicate left and up. 57 | /// 58 | [JsonPropertyName("offset")] 59 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 60 | public double[]? Offset { get; set; } 61 | 62 | /// 63 | /// Marker's opacity when it's in clear view (not behind 3D terrain). 64 | /// 65 | [JsonPropertyName("opacity")] 66 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 67 | public string? Opacity { get; set; } 68 | 69 | /// 70 | /// Marker's opacity when it's behind 3D terrain. 71 | /// 72 | [JsonPropertyName("opacityWhenCovered")] 73 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 74 | public string? OpacityWhenCovered { get; set; } 75 | 76 | /// 77 | /// 'map' aligns the Marker to the plane of the map. 78 | /// 'viewport' aligns the Marker to the plane of the viewport. 79 | /// 'auto' automatically matches the value of rotationAlignment. 80 | /// 81 | [JsonPropertyName("pitchAlignment")] 82 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 83 | public MarkerAlignment? PitchAlignment { get; set; } 84 | 85 | /// 86 | /// The rotation angle of the marker in degrees, relative to its respective rotationAlignment setting. 87 | /// A positive value will rotate the marker clockwise. 88 | /// 89 | [JsonPropertyName("rotation")] 90 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 91 | public double? Rotation { get; set; } 92 | 93 | /// 94 | /// 'map' aligns the Marker's rotation relative to the map, maintaining a bearing as the map rotates. 95 | /// 'viewport' aligns the Marker's rotation relative to the viewport, agnostic to map rotations. 96 | /// 'auto' is equivalent to 'viewport'. 97 | /// 98 | [JsonPropertyName("rotationAlignment")] 99 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 100 | public MarkerAlignment? RotationAlignment { get; set; } 101 | 102 | /// 103 | /// The scale to use for the default marker if options.element is not provided. 104 | /// The default scale corresponds to a height of 41px and a width of 27px. 105 | /// 106 | [JsonPropertyName("scale")] 107 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 108 | public double? Scale { get; set; } 109 | 110 | /// 111 | /// If true, rounding is disabled for placement of the marker, allowing for 112 | /// subpixel positioning and smoother movement when the marker is translated. 113 | /// 114 | [JsonPropertyName("subpixelPositioning")] 115 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 116 | public bool? SubpixelPositioning { get; set; } 117 | } 118 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Padding/PaddingOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Padding; 4 | 5 | public class PaddingOptions 6 | { 7 | /// 8 | /// Padding in pixels from the top of the map canvas. 9 | /// 10 | [JsonPropertyName("top")] 11 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 12 | public double? Top { get; set; } 13 | 14 | /// 15 | /// Padding in pixels from the bottom of the map canvas. 16 | /// 17 | [JsonPropertyName("bottom")] 18 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 19 | public double? Bottom { get; set; } 20 | 21 | /// 22 | /// Padding in pixels from the right of the map canvas. 23 | /// 24 | [JsonPropertyName("right")] 25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 26 | public double? Right { get; set; } 27 | 28 | /// 29 | /// Padding in pixels from the left of the map canvas. 30 | /// 31 | [JsonPropertyName("left")] 32 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 33 | public double? Left { get; set; } 34 | 35 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/PointLike.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | /// 6 | /// Represents a point-like object as either an object with named properties (X, Y) 7 | /// or an array-like structure (two numbers). 8 | /// 9 | public class PointLike(double x, double y) 10 | { 11 | [JsonPropertyName("x")] 12 | public double X { get; set; } = x; 13 | 14 | [JsonPropertyName("y")] 15 | public double Y { get; set; } = y; 16 | 17 | public static PointLike FromArray(double[] coordinates) 18 | { 19 | if (coordinates.Length != 2) 20 | { 21 | throw new ArgumentException("Array must have exactly two elements."); 22 | } 23 | 24 | return new PointLike(coordinates[0], coordinates[1]); 25 | } 26 | 27 | public override string ToString() => $"PointLike({X}, {Y})"; 28 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Popup.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | public class Popup 6 | { 7 | [JsonPropertyName("content")] 8 | public required string Content { get; set; } 9 | 10 | [JsonPropertyName("lngLat")] 11 | public required LngLat Coordinates { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/PopupOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | public class PopupOptions 6 | { 7 | /// 8 | /// Space-separated CSS class names to add to the popup container. 9 | /// 10 | [JsonPropertyName("className")] 11 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 12 | public string? ClassName { get; set; } 13 | 14 | /// 15 | /// If true, a close button will appear in the top right corner of the popup. 16 | /// 17 | /// Default value: true 18 | [JsonPropertyName("closeButton")] 19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 20 | public bool? CloseButton { get; set; } 21 | 22 | /// 23 | /// If true, the popup will close when the map is clicked. 24 | /// 25 | /// Default value: true 26 | [JsonPropertyName("closeOnClick")] 27 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 28 | public bool? CloseOnClick { get; set; } 29 | 30 | /// 31 | /// If true, the popup will close when the map moves. 32 | /// 33 | /// Default value: false 34 | [JsonPropertyName("closeOnMove")] 35 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 36 | public bool? CloseOnMove { get; set; } 37 | 38 | /// 39 | /// If true, the popup will try to focus the first focusable element inside the popup. 40 | /// 41 | /// Default value: true 42 | [JsonPropertyName("focusAfterOpen")] 43 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 44 | public bool? FocusAfterOpen { get; set; } 45 | 46 | /// 47 | /// Optional opacity when the location is behind the globe. 48 | /// If a number is provided, it will be converted to a string. 49 | /// 50 | /// Default value: undefined 51 | [JsonPropertyName("locationOccludedOpacity")] 52 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 53 | public object? LocationOccludedOpacity { get; set; } 54 | 55 | /// 56 | /// Sets the CSS property of the popup's maximum width, e.g., 300px. 57 | /// To ensure the popup resizes to fit its content, set this property to none. 58 | /// 59 | /// See: CSS max-width documentation. 60 | /// 61 | /// 62 | /// Default value: 240px 63 | [JsonPropertyName("maxWidth")] 64 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 65 | public string? MaxWidth { get; set; } 66 | 67 | /// 68 | /// A pixel offset applied to the popup's location. 69 | /// 70 | [JsonPropertyName("offset")] 71 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 72 | public object? Offset { get; set; } 73 | 74 | /// 75 | /// Indicates the part of the popup that should be positioned closest to the coordinate set via SetLngLat. 76 | /// Options: center, top, bottom, left, right, top-left, top-right, bottom-left, bottom-right. 77 | /// If unset, the anchor will be dynamically set with a preference for bottom. 78 | /// 79 | [JsonPropertyName("anchor")] 80 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 81 | public PositionAnchor? Anchor { get; set; } 82 | 83 | /// 84 | /// If true, rounding is disabled for placement of the popup, allowing for subpixel positioning and smoother movement when the popup is translated. 85 | /// 86 | /// Default value: false 87 | [JsonPropertyName("subpixelPositioning")] 88 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 89 | public bool? SubpixelPositioning { get; set; } 90 | } 91 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/PositionAnchor.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | public enum PositionAnchor 6 | { 7 | [JsonStringEnumMemberName("center")] 8 | Center, 9 | 10 | [JsonStringEnumMemberName("top")] 11 | Top, 12 | 13 | [JsonStringEnumMemberName("bottom")] 14 | Bottom, 15 | 16 | [JsonStringEnumMemberName("left")] 17 | Left, 18 | 19 | [JsonStringEnumMemberName("right")] 20 | Right, 21 | 22 | [JsonStringEnumMemberName("top-left")] 23 | TopLeft, 24 | 25 | [JsonStringEnumMemberName("top-right")] 26 | TopRight, 27 | 28 | [JsonStringEnumMemberName("bottom-left")] 29 | BottomLeft, 30 | 31 | [JsonStringEnumMemberName("bottom-right")] 32 | BottomRight 33 | } 34 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/ProjectionSpecification.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Nodes; 2 | using System.Text.Json.Serialization; 3 | using Community.Blazor.MapLibre.Converter; 4 | using OneOf; 5 | 6 | namespace Community.Blazor.MapLibre.Models; 7 | 8 | /// 9 | /// Configuration for the map projection. Defines how the map is projected from geographical coordinates to screen coordinates. 10 | /// 11 | public class ProjectionSpecification 12 | { 13 | /// 14 | /// The projection definition. Can be a string (e.g., "mercator", "globe", "naturalEarth"), 15 | /// a transition object, or an expression (e.g., an interpolation by zoom level). 16 | /// Defaults to "mercator". 17 | /// 18 | [JsonPropertyName("type")] 19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 20 | [JsonConverter(typeof(OneOfJsonConverter))] 21 | public OneOf? Type { get; set; } 22 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/GeoJsonSource.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Community.Blazor.MapLibre.Models.Feature; 3 | 4 | namespace Community.Blazor.MapLibre.Models.Sources; 5 | 6 | /// 7 | /// Represents a GeoJSON source. GeoJSON sources provide either inline GeoJSON data or a URL to a GeoJSON file. 8 | /// They can support clustering and other custom behaviors for point features. 9 | /// 10 | public class GeoJsonSource : ISource 11 | { 12 | /// 13 | [JsonPropertyName("type")] 14 | public string Type => "geojson"; 15 | 16 | /// 17 | /// The GeoJSON data, either as an inline object or a URL to an external GeoJSON file. Required. 18 | /// 19 | [JsonPropertyName("data")] 20 | public required IFeature Data { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/ISource.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Community.Blazor.MapLibre.Models.Sources; 5 | 6 | /// 7 | /// Represents the base class for all map data sources. Each source type (e.g., vector, raster, geojson) will inherit from this class. 8 | /// 9 | [JsonDerivedType(typeof(GeoJsonSource))] 10 | [JsonDerivedType(typeof(ImageSource))] 11 | [JsonDerivedType(typeof(RasterTileSource))] 12 | [JsonDerivedType(typeof(VectorTileSource))] 13 | [JsonDerivedType(typeof(VideoSource))] 14 | public interface ISource 15 | { 16 | /// 17 | /// Defines the source type (e.g., vector, raster, geojson, etc.). 18 | /// 19 | [JsonPropertyName("type")] 20 | public string Type { get; } 21 | } 22 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/ImageSource.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Sources; 4 | 5 | /// 6 | /// Represents an image source. Image sources display a rasterized image stretched to fit a specific set of geographical coordinates. 7 | /// 8 | public class ImageSource : ISource 9 | { 10 | /// 11 | [JsonPropertyName("type")] 12 | public string Type => "image"; 13 | 14 | /// 15 | /// The URL to the image. This is used to load the image to be displayed on the map. Required. 16 | /// 17 | [JsonPropertyName("url")] 18 | public string Url { get; set; } = string.Empty; 19 | 20 | /// 21 | /// The geographical coordinates of the four corners of the image, specified in clockwise order: 22 | /// top left, top right, bottom right, bottom left. Required. 23 | /// Each corner is specified as an array containing `[longitude, latitude]`. 24 | /// 25 | [JsonPropertyName("coordinates")] 26 | public List> Coordinates { get; set; } = []; 27 | } 28 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/QuerySourceFeatureOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Sources; 4 | 5 | /// 6 | /// The options object used with to filter the results. 7 | /// 8 | public class QuerySourceFeatureOptions 9 | { 10 | /// 11 | /// A filter to limit query results. 12 | /// The syntax must follow the MapLibre Style Specification. 13 | /// 14 | [JsonPropertyName("filter")] 15 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 16 | public object? Filter { get; set; } 17 | 18 | /// 19 | /// The name of the source layer to query. 20 | /// 21 | /// For vector tile sources, this parameter is required. For GeoJSON sources, it is ignored. 22 | /// 23 | /// 24 | [JsonPropertyName("sourceLayer")] 25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 26 | public string? SourceLayer { get; set; } 27 | 28 | /// 29 | /// Whether to validate that the filter conforms to the MapLibre Style Specification. 30 | /// 31 | /// Disabling validation can improve performance but should only be done if the values are already known to be valid. 32 | /// 33 | /// 34 | /// Default is true. 35 | [JsonPropertyName("validate")] 36 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 37 | public bool? Validate { get; set; } 38 | } 39 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/RasterTileSource.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Sources; 4 | 5 | /// 6 | /// Represents a raster tile source. Raster sources provide tiled raster image data, typically used for basemaps. 7 | /// 8 | public class RasterTileSource : ISource 9 | { 10 | /// 11 | [JsonPropertyName("type")] 12 | public string Type => "raster"; 13 | 14 | /// 15 | /// URL to a TileJSON resource providing metadata about this source. Optional. 16 | /// 17 | [JsonPropertyName("url")] 18 | public string? Url { get; set; } 19 | 20 | /// 21 | /// An array of URLs to the raster tiles. URL patterns can use placeholders like `{z}`, `{x}`, and `{y}`. Optional. 22 | /// 23 | [JsonPropertyName("tiles")] 24 | public List? Tiles { get; set; } 25 | 26 | /// 27 | /// The bounding box for the source, specified as an array `[sw.lng, sw.lat, ne.lng, ne.lat]`. Optional. 28 | /// 29 | [JsonPropertyName("bounds")] 30 | public double[]? Bounds { get; set; } 31 | } 32 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/VectorTileSource.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Sources; 4 | 5 | /// 6 | /// Represents a vector tile source. Vector sources provide tiled vector data in Mapbox Vector Tile format. 7 | /// 8 | public class VectorTileSource : ISource 9 | { 10 | /// 11 | [JsonPropertyName("type")] 12 | public string Type => "vector"; 13 | 14 | /// 15 | /// URL to a TileJSON resource providing metadata about this source. Optional. 16 | /// 17 | [JsonPropertyName("url")] 18 | public string? Url { get; set; } 19 | 20 | /// 21 | /// An array of URLs to the vector tiles. URL patterns can use placeholders like `{z}`, `{x}`, and `{y}`. Optional. 22 | /// 23 | [JsonPropertyName("tiles")] 24 | public List? Tiles { get; set; } 25 | 26 | /// 27 | /// The bounding box for the source, specified as an array `[sw.lng, sw.lat, ne.lng, ne.lat]`. Optional. 28 | /// 29 | [JsonPropertyName("bounds")] 30 | public double[]? Bounds { get; set; } 31 | 32 | /// 33 | /// The tiling scheme, either `xyz` (standard Slippy map tilenames) or `tms` (OSGeo TMS). Default is `xyz`. Optional. 34 | /// 35 | [JsonPropertyName("scheme")] 36 | public string? Scheme { get; set; } = "xyz"; 37 | 38 | /// 39 | /// Minimum zoom level for which tiles are available, as in the TileJSON spec. 40 | /// Default is 0. Optional. 41 | /// 42 | [JsonPropertyName("minzoom")] 43 | public float? MinZoom { get; set; } 44 | 45 | /// 46 | /// Maximum zoom level for which tiles are available, as in the TileJSON spec. Data from tiles at the maxzoom are used when displaying the map at higher zoom levels. 47 | /// Default is 22. Optional. 48 | /// 49 | [JsonPropertyName("maxzoom")] 50 | public float? MaxZoom { get; set; } 51 | } 52 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sources/VideoSource.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Sources; 4 | 5 | /// 6 | /// Represents a video source. Video sources provide video content to be displayed in specific geographical bounds. 7 | /// 8 | public class VideoSource : ISource 9 | { 10 | /// 11 | [JsonPropertyName("type")] 12 | public string Type => "video"; 13 | 14 | /// 15 | /// URLs to the video content. Multiple URLs should be provided for format compatibility across browsers. Required. 16 | /// 17 | [JsonPropertyName("urls")] 18 | public List Urls { get; set; } = []; 19 | 20 | /// 21 | /// The geographical coordinates of the four corners of the video, specified in clockwise order: 22 | /// top left, top right, bottom right, bottom left. Required. 23 | /// 24 | [JsonPropertyName("coordinates")] 25 | public List> Coordinates { get; set; } = []; 26 | } 27 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Sprite/StyleSetterOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models.Sprite; 4 | 5 | /// 6 | /// Supporting type to add validation to another style related type 7 | /// 8 | public class StyleSetterOptions 9 | { 10 | /// 11 | /// Whether to check if the filter conforms to the MapLibre Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function. 12 | /// 13 | [JsonPropertyName("validate")] 14 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] 15 | public bool? Validate { get; set; } 16 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/TextFit.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | /// 6 | /// Enumeration of possible values for StyleImageMetadata.textFitWidth and textFitHeight. 7 | /// 8 | public enum TextFit 9 | { 10 | /// 11 | /// The image will be resized on the specified axis to fit the content rectangle to the target text 12 | /// and will resize the other axis to maintain the aspect ratio of the content rectangle. 13 | /// 14 | [JsonStringEnumMemberName("proportional")] 15 | Proportional, 16 | 17 | /// 18 | /// The image will be resized on the specified axis to fit the content rectangle to the target text, 19 | /// but will not fall below the aspect ratio of the original content rectangle if the other axis is set to proportional. 20 | /// 21 | [JsonStringEnumMemberName("stretchOnly")] 22 | StretchOnly, 23 | 24 | /// 25 | /// The image will be resized on the specified axis to tightly fit the content rectangle to the target text. 26 | /// This is the same as not being defined. 27 | /// 28 | [JsonStringEnumMemberName("stretchOrShrink")] 29 | StretchOrShrink 30 | } 31 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/Visibility.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Community.Blazor.MapLibre.Models; 4 | 5 | public enum Visibility 6 | { 7 | [JsonStringEnumMemberName("visible")] 8 | Visible, 9 | 10 | [JsonStringEnumMemberName("none")] 11 | None 12 | } 13 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/Models/WebGLContextAttributes.cs: -------------------------------------------------------------------------------- 1 | namespace Community.Blazor.MapLibre.Models; 2 | 3 | /// 4 | /// WebGL context attributes for the map. 5 | /// 6 | public class WebGLContextAttributes 7 | { 8 | public bool? Antialias { get; set; } 9 | public string PowerPreference { get; set; } = "high-performance"; 10 | public bool? PreserveDrawingBuffer { get; set; } 11 | public bool? FailIfMajorPerformanceCaveat { get; set; } 12 | public bool? Desynchronized { get; set; } 13 | public string ContextType { get; set; } = "webgl2withfallback"; 14 | } -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/wwwroot/geojson-antimeridian-cut/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Alexander van de Sandt 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/wwwroot/geojson-antimeridian-cut/cut.js: -------------------------------------------------------------------------------- 1 | import { splitLineString, splitMultiLineString } from './geometry/lines.js'; 2 | import { splitPolygon, splitMultiPolygon } from './geometry/polygons.js'; 3 | import { splitFeature, splitFeatureCollection, splitGeometryCollection } from './geometry/features.js'; 4 | import './util.js'; 5 | 6 | /** 7 | * Recursively up a GeoJSON Object (or its children, if applicable) 8 | * @param {Object} object The GeoJSON Object to split up 9 | * @param {string} object.type 10 | * @param {number[]} [object.coordinates] 11 | * @param {Object[]} [object.features] 12 | * @param {Object[]} [object.geometries] 13 | * 14 | * @returns {Object} The processed `object` 15 | */ 16 | const splitGeoJSON = (object) => { 17 | if (!object.type) { 18 | throw new Error('Object is not a valid GeoJSON Object, must have \'type\' property'); 19 | } 20 | 21 | if (typeof object.type !== 'string') { 22 | throw new Error(`Property 'type' of GeoJSON Object must be 'string', is ${object.type}`); 23 | } 24 | 25 | switch (object.type) { 26 | case 'LineString': 27 | return splitLineString(object); 28 | case 'MultiLineString': 29 | return splitMultiLineString(object); 30 | case 'Polygon': 31 | return splitPolygon(object); 32 | case 'MultiPolygon': 33 | return splitMultiPolygon(object); 34 | case 'Feature': 35 | return splitFeature(object); 36 | case 'FeatureCollection': 37 | return splitFeatureCollection(object); 38 | case 'GeometryCollection': 39 | return splitGeometryCollection(object); 40 | case 'Point': 41 | case 'MultiPoint': 42 | // Do not change these types of GeoJSON Objects 43 | return object; 44 | default: 45 | throw new Error(`Invalid 'type' of GeoJSON object: ${object.type}`); 46 | } 47 | }; 48 | 49 | export default splitGeoJSON; 50 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/wwwroot/geojson-antimeridian-cut/geometry/features.js: -------------------------------------------------------------------------------- 1 | import {splitLineString, splitMultiLineString} from './lines.js'; 2 | import {splitPolygon, splitMultiPolygon} from './polygons.js'; 3 | 4 | const splitGeometryObject = (geometryObject) => { 5 | switch (geometryObject.type) { 6 | case 'LineString': 7 | return splitLineString(geometryObject); 8 | case 'MultiLineString': 9 | return splitMultiLineString(geometryObject); 10 | case 'Polygon': 11 | return splitPolygon(geometryObject); 12 | case 'MultiPolygon': 13 | return splitMultiPolygon(geometryObject); 14 | case 'GeometryCollection': 15 | // eslint-disable-next-line no-use-before-define 16 | return splitGeometryCollection(geometryObject); 17 | case 'Point': 18 | case 'MultiPoint': 19 | // Do not change these types of GeoJSON Objects 20 | return geometryObject; 21 | default: 22 | throw new Error(`Must be some geometry object, is ${geometryObject.type}`); 23 | } 24 | }; 25 | 26 | export const splitFeature = (feature) => { 27 | const { 28 | geometry, 29 | properties, 30 | type: _type, 31 | ...rest 32 | } = feature; 33 | 34 | const rtnGeometry = (geometry) ? splitGeometryObject(geometry) : null; 35 | 36 | return { 37 | type: 'Feature', 38 | geometry: rtnGeometry, 39 | properties, 40 | ...rest, 41 | }; 42 | }; 43 | 44 | export const splitFeatureCollection = (featureCollection) => { 45 | const {features, type: _type, ...rest} = featureCollection; 46 | 47 | return { 48 | type: 'FeatureCollection', 49 | features: features.map(splitFeature), 50 | ...rest, 51 | }; 52 | }; 53 | 54 | export const splitGeometryCollection = (geometryCollection) => { 55 | const {geometries, type: _type, ...rest} = geometryCollection; 56 | 57 | return { 58 | type: 'GeometryCollection', 59 | geometries: geometries.map(splitGeometryObject), 60 | ...rest, 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/wwwroot/geojson-antimeridian-cut/geometry/lines.js: -------------------------------------------------------------------------------- 1 | import {crossingPoints, antimeridianIntersect} from '../util.js'; 2 | 3 | /** 4 | * A LineString GeoJSON Object 5 | * @typedef {Object} LineString 6 | * @property {string} type 7 | * @property {number[][]} coordinates 8 | */ 9 | 10 | /** 11 | * A MultiLineString GeoJSON Object 12 | * @typedef {Object} MultiLineString 13 | * @property {string} type 14 | * @property {number[][][]} coordinates 15 | */ 16 | 17 | /** 18 | * Splits up an array of coordinates into an array of arrays of coordinates. The fundamental 19 | * function for breaking up (Multi)?LineStrings 20 | * 21 | * @param {number[][]} coordinates The list of coordinates to try to split 22 | * @returns {number[][][]} 23 | */ 24 | export const splitCoordinateArray = (coordinates) => { 25 | const crossings = crossingPoints(coordinates); 26 | if (crossings.length === 0) { 27 | return [coordinates]; 28 | } 29 | 30 | const rtn = []; 31 | 32 | // Split up into segments on each side of meridian 33 | rtn.push(coordinates.slice(0, crossings[0] + 1)); 34 | for (let i = 1; i < crossings.length; i += 1) { 35 | rtn.push(coordinates.slice(crossings[i - 1] + 1, crossings[i] + 1)); 36 | } 37 | rtn.push(coordinates.slice(crossings[crossings.length - 1] + 1)); 38 | 39 | // Add in the points on the meridian itself 40 | for (let i = 1; i < rtn.length; i += 1) { 41 | const left = rtn[i - 1]; 42 | const lastLeft = left[left.length - 1]; 43 | const right = rtn[i]; 44 | const firstRight = right[0]; 45 | 46 | const intersect = antimeridianIntersect(lastLeft, firstRight); 47 | 48 | left.push([180 * Math.sign(lastLeft[0]), intersect]); 49 | right.unshift([180 * Math.sign(firstRight[0]), intersect]); 50 | } 51 | 52 | return rtn; 53 | }; 54 | 55 | /** 56 | * Examines the `coordinates` of `lineString` to see if it would cross the antimeridian, and if so, 57 | * transforms it into an equivalent `MultiLineString` broken up over the antimeridian. 58 | * 59 | * @param {LineString} lineString A `LineString` GeoJSON object to break up 60 | * @returns {LineString|MultiLineString} 61 | */ 62 | export const splitLineString = (lineString) => { 63 | const {coordinates, type: _type, ...rest} = lineString; 64 | 65 | const crossings = crossingPoints(coordinates); 66 | 67 | if (crossings.length === 0) { 68 | return { 69 | type: 'LineString', 70 | coordinates, 71 | ...rest, 72 | }; 73 | } 74 | 75 | return { 76 | type: 'MultiLineString', 77 | coordinates: splitCoordinateArray(coordinates), 78 | ...rest, 79 | }; 80 | }; 81 | 82 | /** 83 | * Examines the `coordinates` of `multiLineString` to see if any of its components would cross the 84 | * antimeridian, and if so, transforms it into an equivalent `MultiLineString` broken up over the 85 | * antimeridian. 86 | * 87 | * @param {MultiLineString} multiLineString A `MultiLineString` GeoJSON Object to break up 88 | * @returns {MultiLineString} 89 | */ 90 | export const splitMultiLineString = (multiLineString) => { 91 | const {coordinates, type: _type, ...rest} = multiLineString; 92 | 93 | return { 94 | type: 'MultiLineString', 95 | coordinates: coordinates.map(splitCoordinateArray).flat(1), 96 | ...rest, 97 | }; 98 | }; 99 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/wwwroot/geojson-antimeridian-cut/geometry/polygons.js: -------------------------------------------------------------------------------- 1 | import { 2 | crossingPoints, 3 | antimeridianIntersect, 4 | } from '../util.js'; 5 | 6 | /** 7 | * A Polygon GeoJSON object 8 | * @typedef {Object} Polygon 9 | * @property {string} type 10 | * @property {LinearRing[]} coordinates 11 | */ 12 | 13 | /** 14 | * A MultiPolygon GeoJSON object 15 | * @typedef {Object} MultiPolygon 16 | * @property {string} type 17 | * @property {LinearRing[][]} coordinates 18 | */ 19 | 20 | /** 21 | * An array of four or more coordinates that defines the boundary of a surface or a hole in a 22 | * surface. The first coordinate must be equivalent to the last. 23 | * @typedef {number[][]} LinearRing 24 | */ 25 | 26 | /** 27 | * @param {LinearRing} linearRing 28 | * @param {LinearRing[]} collector 29 | */ 30 | const splitLinearRing = (linearRing, collector) => { 31 | const crossings = crossingPoints(linearRing); 32 | if (crossings.length === 0) { 33 | collector.push([linearRing]); 34 | return; 35 | } 36 | 37 | // Get two highest-latitude intersection points 38 | const [startIntersect, endIntersect] = crossings 39 | .map(i => [ 40 | antimeridianIntersect(linearRing[i], linearRing[i + 1]), 41 | i, 42 | ]) 43 | .sort(([latA], [latB]) => latB - latA); 44 | 45 | // Traverse from first intersection and build 'left' polygon 46 | const leftRing = []; 47 | // Add the first point (the intersection point) 48 | let currentIndex = startIntersect[1] + 1; 49 | if (currentIndex === linearRing.length - 1) { 50 | currentIndex = 0; 51 | } 52 | let firstAfterIntersect = linearRing[currentIndex]; 53 | 54 | leftRing.push([180 * Math.sign(firstAfterIntersect[0]), startIntersect[0]]); 55 | leftRing.push(linearRing[currentIndex]); 56 | while (currentIndex !== endIntersect[1]) { 57 | currentIndex += 1; 58 | if (currentIndex === linearRing.length - 1) { // End of ring, wrap around 59 | currentIndex = 0; 60 | } 61 | 62 | leftRing.push(linearRing[currentIndex]); 63 | } 64 | leftRing.push([180 * Math.sign(firstAfterIntersect[0]), endIntersect[0]]); 65 | 66 | // Traverse from second intersection and build 'right' polygon; 67 | const rightRing = []; 68 | currentIndex = endIntersect[1] + 1; 69 | if (currentIndex === linearRing.length - 1) { 70 | currentIndex = 0; 71 | } 72 | firstAfterIntersect = linearRing[currentIndex]; 73 | 74 | rightRing.push([180 * Math.sign(firstAfterIntersect[0]), endIntersect[0]]); 75 | rightRing.push(linearRing[currentIndex]); 76 | while (currentIndex !== startIntersect[1]) { 77 | currentIndex += 1; 78 | if (currentIndex === linearRing.length - 1) { 79 | currentIndex = 0; 80 | } 81 | 82 | rightRing.push(linearRing[currentIndex]); 83 | } 84 | rightRing.push([180 * Math.sign(firstAfterIntersect[0]), startIntersect[0]]); 85 | 86 | // Duplicate first value at end to create a ring 87 | leftRing.push(leftRing[0]); 88 | rightRing.push(rightRing[0]); 89 | 90 | splitLinearRing(leftRing, collector); 91 | splitLinearRing(rightRing, collector); 92 | }; 93 | 94 | /** 95 | * Splits a polygon's linear rings. The first ring in the list is treated as the outer loop. The 96 | * remaining rings, if they exist, are treated as holes. If the main ring is split, all holes are 97 | * added to all resulting polygons. If a hole is split, the resulting rings are also applied to all 98 | * top-level polygons. 99 | * 100 | * @param {LinearRing[]} rings A list of rings to spli 101 | * @returns {LinearRing[][]} 102 | */ 103 | const splitPolygonRingList = (rings) => { 104 | const [mainRing, ...holes] = rings; 105 | 106 | const rtn = []; 107 | 108 | // Split the main ring & push results into rtn value 109 | splitLinearRing(mainRing, rtn); 110 | 111 | // Split each hole and apply the cut holes to each resulting ring 112 | if (holes) { 113 | const cutHoles = []; 114 | holes.forEach(hole => splitLinearRing(hole, cutHoles)); 115 | rtn.forEach(ring => cutHoles.flat().forEach(hole => ring.push(hole))); 116 | } 117 | 118 | return rtn; 119 | }; 120 | 121 | /** 122 | * @param {Polygon} polygon 123 | * @returns {(MultiPolygon|Polygon)} 124 | */ 125 | export const splitPolygon = (polygon) => { 126 | const {coordinates: linearRings, type: _type, ...rest} = polygon; 127 | 128 | const cutPolygon = splitPolygonRingList(linearRings); 129 | return (cutPolygon.length > 1) 130 | ? { 131 | type: 'MultiPolygon', 132 | coordinates: cutPolygon, 133 | ...rest, 134 | } 135 | : { 136 | type: 'Polygon', 137 | coordinates: cutPolygon.flat(), 138 | ...rest, 139 | }; 140 | }; 141 | 142 | /** 143 | * @param {MultiPolygon} multiPolygon 144 | * @returns {MultiPolygon} 145 | */ 146 | export const splitMultiPolygon = (multiPolygon) => { 147 | const {coordinates: polygons, type: _type, ...rest} = multiPolygon; 148 | 149 | const rtn = polygons.map(polygon => splitPolygonRingList(polygon)).flat(); 150 | 151 | return { 152 | type: 'MultiPolygon', 153 | coordinates: rtn, 154 | ...rest, 155 | }; 156 | }; 157 | -------------------------------------------------------------------------------- /Community.Blazor.MapLibre/wwwroot/geojson-antimeridian-cut/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @example 3 | * bringLonWithinBounds(1) === 1; 4 | * bringLonWithinBounds(181) === 1; 5 | * bringLonWithinBounds(-365) === -5; 6 | * 7 | * @param {number} lon The longitude to reduce 8 | * @returns {number} A longitude equivalent to `lon` between [-180, 180] 9 | */ 10 | export const bringLonWithinBounds = (lon) => { 11 | let rtn = lon; 12 | 13 | while (rtn > 180) { 14 | rtn -= 360; 15 | } 16 | 17 | while (rtn < -180) { 18 | rtn += 360; 19 | } 20 | 21 | return rtn; 22 | }; 23 | 24 | /** 25 | * Checks if the line drawn between two points would cross the antimeridian. 26 | * @param {number[]} a The first point to check 27 | * @param {number[]} b The second point to check 28 | * 29 | * @returns {boolean} `true` if the points straddle the antimeridian, `false` otherwise 30 | */ 31 | export const straddlesAntimeridian = ([lonARaw], [lonBRaw]) => { 32 | // If they are more than 360 degrees apart, they must cross the antimeridian. 33 | // Only applies to points with coords > 180 or < -180 34 | if (lonARaw - lonBRaw >= 360 || lonARaw - lonBRaw <= -360) { 35 | return true; 36 | } 37 | 38 | const lonA = bringLonWithinBounds(lonARaw); 39 | const lonB = bringLonWithinBounds(lonBRaw); 40 | 41 | if (Math.sign(lonA) === Math.sign(lonB)) { 42 | return false; 43 | } 44 | 45 | return ((lonA > 0) 46 | ? (lonB < -(180 - lonA)) 47 | : (lonA < -(180 - lonB)) 48 | ); 49 | }; 50 | 51 | /** 52 | * Finds the points at which a list of coordinates crosses the antimeridian 53 | * 54 | * @param {number[][]} points The points to check 55 | * 56 | * @returns {number[]} The indicies of the `points` that occur immediately before a crossing 57 | */ 58 | export const crossingPoints = (points) => { 59 | const rtn = []; 60 | 61 | for (let i = 1; i < points.length; i += 1) { 62 | const a = points[i - 1]; 63 | const b = points[i]; 64 | 65 | if (straddlesAntimeridian(a, b)) { 66 | rtn.push(i - 1); 67 | } 68 | } 69 | 70 | return rtn; 71 | }; 72 | 73 | const makeLonPositive = lon => ((lon < 0) ? makeLonPositive(lon + 360) : lon); 74 | 75 | /** 76 | * Calculates the latitude at which a line drawn between two coordinates would intersect 77 | * the antimeridian. Assumes that: 78 | * * The line would cross the antimeridian 79 | * * -180 < lon{A,B} < 180 80 | * * -90 < lat{A,B} < 90 81 | * 82 | * @param {number[]} a The first point to check, [lon, lat] 83 | * @param {number[]} b The second point to check, [lon, lat] 84 | * 85 | * @returns {number} The Antimeridian Intersect of `a` and `b` 86 | */ 87 | export const antimeridianIntersect = ([lonA, latA], [lonB, latB]) => { 88 | const lonANorm = makeLonPositive(lonA); 89 | const lonBNorm = makeLonPositive(lonB); 90 | 91 | const slope = (latB - latA) / (lonBNorm - lonANorm); 92 | 93 | return slope * (180 - lonANorm) + latA; 94 | }; 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [![Contributors][contributors-shield]][contributors-url] 5 | [![Forks][forks-shield]][forks-url] 6 | [![Stargazers][stars-shield]][stars-url] 7 | [![Unlicense License][license-shield]][license-url] 8 | [![Issues][issues-shield]][issues-url] 9 | [![NuGet][nuget-shield]][nuget-url] 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 | Logo 18 | 19 | 20 |

Blazor.MapLibre

21 | 22 |

23 | A C# wrapper around MapLibre GL JS library 24 |
25 |
26 | View Demo 27 | · 28 | Report Bug 29 | · 30 | Request Feature 31 |

32 |
33 | 34 | 35 | 36 | 37 |
38 | Table of Contents 39 |
    40 |
  1. 41 | About The Project 42 |
  2. 43 |
  3. 44 | Getting Started 45 | 49 |
  4. 50 |
  5. Usage
  6. 51 |
  7. Roadmap
  8. 52 |
  9. Contributing
  10. 53 |
  11. Disclaimer
  12. 54 |
  13. License
  14. 55 |
  15. Acknowledgments
  16. 56 |
57 |
58 | 59 | 60 | 61 | 62 | ## About The Project 63 | 64 | This project should help people working on Blazor projects to use maps more easily. 65 | 66 |

(back to top)

67 | 68 | 69 | 70 | 71 | ## Getting Started 72 | 73 | ### Prerequisites 74 | 75 | This project is created on .NET 9 so you need to use this or never version to run it. 76 | 77 | ### Installation 78 | 79 | Install the package: 80 | ```bash 81 | dotnet add package Community.Blazor.MapLibre 82 | ``` 83 | 84 | For Blazor Server and Blazor Web App, add this to head of your file to load the css of the maps. 85 | ```html 86 | 87 | ``` 88 | 89 |

(back to top)

90 | 91 | 92 | 93 | 94 | ## Usage 95 | 96 | After the package is installed you can use it with simple: 97 | ```csharp 98 | 99 | ``` 100 | 101 | You can customize the map more with options using `MapOptions.cs`: 102 | ```csharp 103 | 104 | 105 | @code 106 | { 107 | private readonly MapOptions _mapOptions = new MapOptions(); 108 | } 109 | ``` 110 | 111 | 112 | 113 | ## Roadmap 114 | - ✅ Completed 115 | - ☑️ Partially implemented (missing C# models,...) 116 | - ❌ Not started 117 | 118 | 119 | | Feature | Status | 120 | |---------|------------------------------| 121 | | **Map** | **☑️ Partially implemented** | 122 | 123 |
124 | Map Implementation 125 | 126 | | Feature | Status | 127 | |-------------------------------------------------------|------------------------------| 128 | | **Map** | **☑️ Partially implemented** | 129 | | - Options | **✅ Complete** | 130 | | **Events** | | 131 | | - on() | **✅ Complete** | 132 | | - off() | **❌ Not started** | 133 | | - once() | **❌ Not started** | 134 | | **Methods** | | 135 | | - addControl() | **☑️ Partially Implemented** | 136 | | - addImage() | **☑️ Partially Implemented** | 137 | | - addLayer() | **✅ Complete** | 138 | | - addSource() | **✅ Complete** | 139 | | - addSprite() | **☑️ Partially Implemented** | 140 | | - areTilesLoaded() | **✅ Complete** | 141 | | - calculateCameraOptionsFromCameraLngLatAltRotation() | **✅ Complete** | 142 | | - calculateCameraOptionsFromTo() | **✅ Complete** | 143 | | - cameraForBounds() | **☑️ Partially Implemented** | 144 | | - easeTo() | **✅ Complete** | 145 | | - fitBounds() | **✅ Complete** | 146 | | - fitScreenCoordinates() | **✅ Complete** | 147 | | - flyTo() | **✅ Complete** | 148 | | - getBearing() | **✅ Complete** | 149 | | - getBounds() | **✅ Complete** | 150 | | - getCameraTargetElevation() | **✅ Complete** | 151 | | - getCanvas() | **✅ Complete** | 152 | | - getCanvasContainer() | **✅ Complete** | 153 | | - getCenter() | **✅ Complete** | 154 | | - getCenterClampedToGround() | **✅ Complete** | 155 | | - getCenterElevation() | **✅ Complete** | 156 | | - getContainer() | **✅ Complete** | 157 | | - getFeatureState() | **☑️ Partially Implemented** | 158 | | - getFilter() | **✅ Complete** | 159 | | - getGlyphs() | **✅ Complete** | 160 | | - getImage() | **✅ Complete** | 161 | | - getLayer() | **✅ Complete** | 162 | | - getLayersOrder() | **✅ Complete** | 163 | | - getLayoutProperty() | **☑️ Partially Implemented** | 164 | | - getLight() | **☑️ Partially Implemented** | 165 | | - getMaxBounds() | **☑️ Partially Implemented** | 166 | | - getMaxPitch() | **✅ Complete** | 167 | | - getMaxZoom() | **✅ Complete** | 168 | | - getMinPitch() | **✅ Complete** | 169 | | - getMinZoom() | **✅ Complete** | 170 | | - getPadding() | **☑️ Partially Implemented** | 171 | | - getPaintProperty() | **☑️ Partially Implemented** | 172 | | - getPitch() | **✅ Complete** | 173 | | - getPixelRatio() | **✅ Complete** | 174 | | - getProjection() | **☑️ Partially Implemented** | 175 | | - getRenderWorldCopies() | **✅ Complete** | 176 | | - getRoll() | **✅ Complete** | 177 | | - getSky() | **☑️ Partially Implemented** | 178 | | - getSource() | **☑️ Partially Implemented** | 179 | | - getSprite() | **☑️ Partially Implemented** | 180 | | - getStyle() | **☑️ Partially Implemented** | 181 | | - getTerrain() | **☑️ Partially Implemented** | 182 | | - getVerticalFieldOfView() | **✅ Complete** | 183 | | - getZoom() | **✅ Complete** | 184 | | - hasControl() | **☑️ Partially Implemented** | 185 | | - hasImage() | **✅ Complete** | 186 | | - isMoving() | **✅ Complete** | 187 | | - isRotating() | **✅ Complete** | 188 | | - isSourceLoaded() | **✅ Complete** | 189 | | - isStyleLoaded() | **✅ Complete** | 190 | | - isZooming() | **✅ Complete** | 191 | | - jumpTo() | **✅ Complete** | 192 | | - listens() | **✅ Complete** | 193 | | - listImages() | **✅ Complete** | 194 | | - loaded() | **✅ Complete** | 195 | | - loadImage() | **☑️ Partially Implemented** | 196 | | - moveLayer() | **✅ Complete** | 197 | | - panBy() | **✅ Complete** | 198 | | - panTo() | **✅ Complete** | 199 | | - project() | **✅ Complete** | 200 | | - queryRenderedFeatures() | **☑️ Partially Implemented** | 201 | | - querySourceFeatures() | **✅ Complete** | 202 | | - queryTerrainElevation() | **✅ Complete** | 203 | | - redraw() | **✅ Complete** | 204 | | - remove() | **✅ Complete** | 205 | | - removeControl() | **☑️ Partially Implemented** | 206 | | - removeFeatureState() | **✅ Complete** | 207 | | - removeImage() | **✅ Complete** | 208 | | - removeLayer() | **✅ Complete** | 209 | | - removeSource() | **✅ Complete** | 210 | | - removeSprite() | **✅ Complete** | 211 | | - resetNorth() | **✅ Complete** | 212 | | - resetNorthPitch() | **✅ Complete** | 213 | | - resize() | **✅ Complete** | 214 | | - rotateTo() | **✅ Complete** | 215 | | - setBearing() | **✅ Complete** | 216 | | - setCenter() | **✅ Complete** | 217 | | - setCenterClampedToGround() | **✅ Complete** | 218 | | - setCenterElevation() | **✅ Complete** | 219 | | - setFeatureState() | **✅ Complete** | 220 | | - setZoom() | **✅ Complete** | 221 | | - setStyle() | **☑️ Partially Implemented** | 222 | | - stop() | **✅ Complete** | 223 | | - unproject() | **✅ Complete** | 224 | | - updateImage() | **☑️ Partially Implemented** | 225 | | - zoomIn() | **✅ Complete** | 226 | | - zoomOut() | **✅ Complete** | 227 | | - zoomTo() | **✅ Complete** | 228 | 229 |
230 | 231 | | Feature | Status | 232 | |-------------|-------------------| 233 | | **Markers** | **✅ Complete** | 234 | 235 | | Feature | Status | 236 | |-----------|------------------| 237 | | **Popup** | **✅ Complete** | 238 | 239 | 240 | 241 | See the [open issues](https://github.com/Yet-another-solution/Blazor.MapLibre/issues) for a full list of proposed features (and known issues). 242 | 243 |

(back to top)

244 | 245 | 246 | 247 | 248 | ## Contributing 249 | 250 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 251 | 252 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". 253 | Don't forget to give the project a star! Thanks again! 254 | 255 | 1. Fork the Project 256 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 257 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 258 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 259 | 5. Open a Pull Request 260 | 261 | ### Top contributors: 262 | 263 | 264 | contrib.rocks image 265 | 266 | 267 |

(back to top)

268 | 269 | 270 | ## Disclaimer 271 | 272 | **Community.Blazor.MapLibre** is provided "as-is," without warranties of any kind, express or implied. The author(s) make no guarantees regarding the accuracy, reliability, or suitability of the software for any particular purpose. 273 | 274 | By using this library, you acknowledge that: 275 | 276 | - The software is **open-source and freely available**, with no assurances of ongoing maintenance or support. 277 | - The author(s) are **not liable for any damages, data loss, or operational failures** resulting from its use. 278 | - Users assume full responsibility for implementing proper **security and compliance measures** when integrating this library into their applications. 279 | - The software may change over time, and updates may introduce modifications or remove certain features. 280 | 281 | Use of **Community.Blazor.MapLibre** is **at your own risk**, and contributions or modifications by third parties are not officially vetted. If you encounter issues or bugs, reporting them via the official repository is encouraged, but resolution is not guaranteed. 282 | 283 | For licensing details, refer to the **[Unlicense](https://choosealicense.com/licenses/unlicense/)** statement included with the repository. 284 | 285 | 286 | 287 | ## License 288 | 289 | This project, **Community.Blazor.MapLibre**, is released under the **[Unlicense](https://choosealicense.com/licenses/unlicense/)**, meaning it is free and unencumbered software dedicated to the public domain. 290 | 291 | However, this library is a wrapper around **MapLibre GL JS**, which is licensed under the **BSD-3-Clause** license. Users must comply with the original library’s license when using this wrapper. 292 | 293 | For details on MapLibre GL JS licensing, visit: [MapLibre GL JS License](https://github.com/maplibre/maplibre-gl-js/blob/main/LICENSE.txt). 294 | 295 | 296 |

(back to top)

297 | 298 | 299 | 300 | ## Acknowledgments 301 | 302 | Use this space to list resources you find helpful and would like to give credit to. I've included a few of my favorites to kick things off! 303 | 304 | * [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet) 305 | * [Img Shields](https://shields.io) 306 | * [GitHub Pages](https://pages.github.com) 307 | * [Best-README-Template](https://github.com/othneildrew/Best-README-Template) 308 | 309 |

(back to top)

310 | 311 | 312 | 313 | 314 | 315 | [contributors-shield]: https://img.shields.io/github/contributors/Yet-another-solution/Blazor.MapLibre.svg?style=for-the-badge 316 | [contributors-url]: https://github.com/Yet-another-solution/Blazor.MapLibre/graphs/contributors 317 | [forks-shield]: https://img.shields.io/github/forks/Yet-another-solution/Blazor.MapLibre.svg?style=for-the-badge 318 | [forks-url]: https://github.com/Yet-another-solution/Blazor.MapLibre/network/members 319 | [stars-shield]: https://img.shields.io/github/stars/Yet-another-solution/Blazor.MapLibre.svg?style=for-the-badge 320 | [stars-url]: https://github.com/Yet-another-solution/Blazor.MapLibre/stargazers 321 | [issues-shield]: https://img.shields.io/github/issues/Yet-another-solution/Blazor.MapLibre.svg?style=for-the-badge 322 | [issues-url]: https://github.com/Yet-another-solution/Blazor.MapLibre/issues 323 | [license-shield]: https://img.shields.io/github/license/Yet-another-solution/Blazor.MapLibre.svg?style=for-the-badge 324 | [license-url]: https://github.com/Yet-another-solution/Blazor.MapLibre/blob/master/LICENSE.txt 325 | [nuget-shield]: https://img.shields.io/nuget/v/Community.Blazor.MapLibre.svg?style=for-the-badge 326 | [nuget-url]: https://www.nuget.org/packages/Community.Blazor.MapLibre 327 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.0", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": true 6 | } 7 | } --------------------------------------------------------------------------------