diff --git a/.editorconfig b/.editorconfig
index 61536f81..e3771e59 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,4 +1,4 @@
-# Remove the line below if you want to inherit .editorconfig settings from higher directories
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# All files
@@ -131,7 +131,7 @@ csharp_prefer_simple_using_statement = false
csharp_prefer_system_threading_lock = true
csharp_style_namespace_declarations = file_scoped
csharp_style_prefer_method_group_conversion = false
-csharp_style_prefer_primary_constructors = true
+csharp_style_prefer_primary_constructors = false
csharp_style_prefer_top_level_statements = false
# Expression-level preferences
diff --git a/.github/actions/documentation/docfx-build/action.yml b/.github/actions/documentation/docfx-build/action.yml
index 2b9ba79f..e21ced83 100644
--- a/.github/actions/documentation/docfx-build/action.yml
+++ b/.github/actions/documentation/docfx-build/action.yml
@@ -22,7 +22,7 @@ runs:
using: composite
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Dotnet Setup
uses: actions/setup-dotnet@v4
with:
@@ -41,7 +41,7 @@ runs:
run: docfx build ${{ inputs.docfx-json-manifest }}
shell: bash
- name: Upload artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.output-directory }}
diff --git a/.github/actions/documentation/docfx-metadata/action.yml b/.github/actions/documentation/docfx-metadata/action.yml
index bcfb67d2..bbcd6383 100644
--- a/.github/actions/documentation/docfx-metadata/action.yml
+++ b/.github/actions/documentation/docfx-metadata/action.yml
@@ -26,7 +26,7 @@ runs:
using: composite
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Dotnet Setup
uses: actions/setup-dotnet@v4
with:
@@ -59,7 +59,7 @@ runs:
mkdir -p ${{ inputs.output-directory }}
cp -r ${{ inputs.temporary-directory }}/* ${{ inputs.output-directory }}
- name: 'Upload artifact'
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.output-directory }}
diff --git a/.github/actions/git/push-changes/action.yml b/.github/actions/git/push-changes/action.yml
index a5799d2a..31ddd3f2 100644
--- a/.github/actions/git/push-changes/action.yml
+++ b/.github/actions/git/push-changes/action.yml
@@ -28,7 +28,7 @@ runs:
using: "composite"
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Dotnet Setup
uses: actions/setup-dotnet@v4
@@ -37,7 +37,7 @@ runs:
- name: Download a single artifact
if: ${{ inputs.artifact-name != '' }}
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.working-directory }}
diff --git a/.github/actions/github/create-release/action.yml b/.github/actions/github/create-release/action.yml
index f714fc5b..0e0015ce 100644
--- a/.github/actions/github/create-release/action.yml
+++ b/.github/actions/github/create-release/action.yml
@@ -1,4 +1,4 @@
-name: 'Create GitHub release'
+name: 'Create GitHub release'
author: 'Pete Sramek'
description: 'Create GitHub release.'
inputs:
@@ -17,7 +17,7 @@ runs:
using: composite
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- run: |
echo "release-version=${{ inputs.release-version }}"
echo "is-preview=${{ inputs.is-preview }}"
@@ -25,6 +25,10 @@ runs:
echo "notes-start-tag=${{ inputs.notes-start-tag }}"
echo "notes-start-tag-argument="${{ inputs.notes-start-tag != '' && '--notes-start-tag $(inputs.notes-start-tag)' || '' }}"
shell: bash
+ - name: 'Create git tag ${{ env.release-version }}'
+ shell: bash
+ run: |
+ git tag -a ${{ env.release-version }} -m "${{ env.release-version }}"
- name: 'Create GitHub release PolylineAlgorithm ${{ env.release-version }}'
shell: bash
env:
diff --git a/.github/actions/github/write-file-to-summary/action.yml b/.github/actions/github/write-file-to-summary/action.yml
index ac682f6d..c185ce4b 100644
--- a/.github/actions/github/write-file-to-summary/action.yml
+++ b/.github/actions/github/write-file-to-summary/action.yml
@@ -1,4 +1,4 @@
-name: 'Write file to step summary'
+name: 'Write file to step summary'
author: 'Pete Sramek'
description: 'Writes file contents to step summary.'
inputs:
@@ -17,7 +17,7 @@ runs:
using: composite
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Writing ${{ inputs.file }} to step summary
shell: bash
diff --git a/.github/actions/nuget/publish-package/action.yml b/.github/actions/nuget/publish-package/action.yml
index 64b4885a..eec82f65 100644
--- a/.github/actions/nuget/publish-package/action.yml
+++ b/.github/actions/nuget/publish-package/action.yml
@@ -38,10 +38,10 @@ runs:
exit 1
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Download package artifact
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@v8
with:
name: ${{ inputs.package-artifact-name }}
diff --git a/.github/actions/source/compile/action.yml b/.github/actions/source/compile/action.yml
index 2a59e696..0af331ee 100644
--- a/.github/actions/source/compile/action.yml
+++ b/.github/actions/source/compile/action.yml
@@ -50,7 +50,7 @@ runs:
using: "composite"
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ inputs.dotnet_sdk_version }}'
uses: actions/setup-dotnet@v4
@@ -68,7 +68,7 @@ runs:
- name: 'Upload build artifacts'
if: ${{ inputs.upload-build-artifacts == 'true' }}
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ inputs.build-artifacts-name }}
path: ${{ inputs.build-artifacts-glob-pattern }}
diff --git a/.github/actions/source/format/action.yml b/.github/actions/source/format/action.yml
index 5ce79d8f..de2536c2 100644
--- a/.github/actions/source/format/action.yml
+++ b/.github/actions/source/format/action.yml
@@ -32,7 +32,7 @@ runs:
using: "composite"
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ inputs.dotnet_sdk_version }}'
uses: actions/setup-dotnet@v4
diff --git a/.github/actions/testing/code-coverage/action.yml b/.github/actions/testing/code-coverage/action.yml
index 1e42d2ca..8751a7b2 100644
--- a/.github/actions/testing/code-coverage/action.yml
+++ b/.github/actions/testing/code-coverage/action.yml
@@ -24,7 +24,7 @@ runs:
using: composite
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ inputs.dotnet_sdk_version }}'
uses: actions/setup-dotnet@v4
diff --git a/.github/actions/testing/test-report/action.yml b/.github/actions/testing/test-report/action.yml
index 6dc55d81..40e66385 100644
--- a/.github/actions/testing/test-report/action.yml
+++ b/.github/actions/testing/test-report/action.yml
@@ -25,7 +25,7 @@ runs:
using: composite
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ inputs.dotnet_sdk_version }}'
uses: actions/setup-dotnet@v4
diff --git a/.github/actions/versioning/extract-version/action.yml b/.github/actions/versioning/extract-version/action.yml
index faad6a35..40229ad8 100644
--- a/.github/actions/versioning/extract-version/action.yml
+++ b/.github/actions/versioning/extract-version/action.yml
@@ -28,7 +28,7 @@ runs:
using: "composite"
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ inputs.dotnet_sdk_version }}'
uses: actions/setup-dotnet@v4
diff --git a/.github/actions/versioning/format-version/action.yml b/.github/actions/versioning/format-version/action.yml
index 2b3d11c0..70289ee0 100644
--- a/.github/actions/versioning/format-version/action.yml
+++ b/.github/actions/versioning/format-version/action.yml
@@ -45,7 +45,7 @@ runs:
using: "composite"
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ inputs.dotnet_sdk_version }}'
uses: actions/setup-dotnet@v4
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d0044504..047d4afd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -54,7 +54,7 @@ jobs:
release-version: ${{ steps.format-version.outputs.release-version }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
uses: actions/setup-dotnet@v5
with:
@@ -102,7 +102,7 @@ jobs:
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Format source code'
uses: ./.github/actions/source/format
@@ -121,7 +121,7 @@ jobs:
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Compile source code'
uses: ./.github/actions/source/compile
@@ -138,7 +138,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET'
uses: actions/setup-dotnet@v5
@@ -184,7 +184,7 @@ jobs:
package-artifact-name: ${{ env.package-artifact-name }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -192,7 +192,7 @@ jobs:
dotnet-version: ${{ env.dotnet-sdk-version }}
- name: Download Build
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@v8
with:
name: build
@@ -201,7 +201,7 @@ jobs:
dotnet pack ${{ vars.SRC_DEFAULT_GLOB_PATTERN }} --configuration ${{ env.build-configuration }} /p:Platform="${{ env.build-platform }}" /p:PackageVersion=${{ env.release-version }} /p:Version=${{ env.assembly-version }} /p:AssemblyInformationalVersion=${{ env.assembly-informational-version }} /p:FileVersion=${{ env.file-version }} --output ${{ runner.temp }}/${{ env.nuget-packages-directory }}
- name: Upload Package
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ env.package-artifact-name }}
path: |
@@ -217,7 +217,7 @@ jobs:
environment: 'Development'
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -242,7 +242,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Generate assembly metadata'
uses: ./.github/actions/documentation/docfx-metadata
with:
diff --git a/.github/workflows/promote-branch.yml b/.github/workflows/promote-branch.yml
index 21d999f1..03df4b47 100644
--- a/.github/workflows/promote-branch.yml
+++ b/.github/workflows/promote-branch.yml
@@ -39,7 +39,7 @@ jobs:
friendly-version: ${{ steps.extract-version.outputs.version }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
uses: actions/setup-dotnet@v5
with:
@@ -153,7 +153,7 @@ jobs:
target-branch: ${{ env.target-branch }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Check promotion type'
if: ${{ (env.promotion-type != 'release') && (env.promotion-type != 'preview') }}
run: |
@@ -192,7 +192,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
uses: actions/setup-dotnet@v5
with:
diff --git a/.github/workflows/publish-documentation.yml b/.github/workflows/publish-documentation.yml
index 7f9af8e3..9134bbbe 100644
--- a/.github/workflows/publish-documentation.yml
+++ b/.github/workflows/publish-documentation.yml
@@ -1,4 +1,4 @@
-name: 'Publish documentation'
+name: 'Publish documentation'
on:
workflow_dispatch:
@@ -35,7 +35,7 @@ jobs:
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
fetch-depth: 0 # Ensure the full git history is available for versioning
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
@@ -56,7 +56,7 @@ jobs:
friendly-version: ${{ needs.versioning.outputs.friendly-version }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Generate documentation'
uses: ./.github/actions/documentation/docfx-build
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 85c21862..8ea9622c 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -55,7 +55,7 @@ jobs:
release-version: ${{ steps.format-version.outputs.release-version }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
uses: actions/setup-dotnet@v5
with:
@@ -109,7 +109,7 @@ jobs:
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Compile source code'
uses: ./.github/actions/source/compile
@@ -126,7 +126,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET'
uses: actions/setup-dotnet@v5
@@ -136,7 +136,7 @@ jobs:
- name: 'Run tests'
uses: ./.github/actions/testing/test
with:
- project-path: '**/PolylineAlgorithm.Tests.csproj'
+ project-path: './tests/PolylineAlgorithm.Tests/PolylineAlgorithm.Tests.csproj'
test-results-directory: '${{ runner.temp }}/${{ env.test-result-directory }}/'
code-coverage-settings-file: '${{ github.workspace}}/code-coverage-settings.xml'
@@ -172,7 +172,7 @@ jobs:
package-artifact-name: ${{ env.package-artifact-name }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -180,7 +180,7 @@ jobs:
dotnet-version: ${{ env.dotnet-sdk-version }}
- name: Download Build
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@v8
with:
name: build
@@ -189,7 +189,7 @@ jobs:
dotnet pack ${{ vars.SRC_DEFAULT_GLOB_PATTERN }} --configuration ${{ env.build-configuration }} /p:Platform="${{ env.build-platform }}" /p:PackageVersion=${{ env.release-version }} /p:Version=${{ env.assembly-version }} /p:AssemblyInformationalVersion=${{ env.assembly-informational-version }} /p:FileVersion=${{ env.file-version }} --output ${{ runner.temp }}/${{ env.nuget-packages-directory }}
- name: Upload Package
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ env.package-artifact-name }}
path: |
@@ -205,7 +205,7 @@ jobs:
environment: 'Development'
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -232,7 +232,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Install .NET SDK
uses: actions/setup-dotnet@v5
with:
@@ -240,14 +240,14 @@ jobs:
8.x
10.x
- name: Download Build
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@v8
with:
name: build
- name: Benchmark
working-directory: ${{ vars.BENCHMARKDOTNET_WORKING_DIRECTORY }}
run: dotnet run --configuration ${{ env.build-configuration }} /p:Platform=${{ env.build-platform }} --framework ${{ vars.DEFAULT_BUILD_FRAMEWORK }} --runtimes ${{ vars.BENCHMARKDOTNET_RUNTIMES }} --filter ${{ vars.BENCHMARKDOTNET_FILTER }} --artifacts ${{ runner.temp }}/benchmarks/ --exporters GitHub --memory --iterationTime 100 --join
- name: Upload Benchmark Results
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: benchmark-${{ matrix.os }}
path: |
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6d3066a1..30379531 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ concurrency:
cancel-in-progress: false
env:
- dotnet-sdk-version: '10.x'
+ dotnet-sdk-version: '9.x'
build-configuration: 'Release'
build-platform: 'Any CPU'
git-version: '6.0.x'
@@ -65,7 +65,7 @@ jobs:
release-version: ${{ steps.format-version.outputs.release-version }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET ${{ env.dotnet-sdk-version }}'
uses: actions/setup-dotnet@v5
with:
@@ -119,7 +119,7 @@ jobs:
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Compile source code'
uses: ./.github/actions/source/compile
@@ -136,7 +136,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Setup .NET'
uses: actions/setup-dotnet@v5
@@ -146,7 +146,7 @@ jobs:
- name: 'Run tests'
uses: ./.github/actions/testing/test
with:
- project-path: '**/PolylineAlgorithm.Tests.csproj'
+ project-path: './tests/PolylineAlgorithm.Tests/PolylineAlgorithm.Tests.csproj'
test-results-directory: '${{ runner.temp }}/${{ env.test-result-directory }}/'
code-coverage-settings-file: '${{ github.workspace}}/code-coverage-settings.xml'
@@ -182,7 +182,7 @@ jobs:
package-artifact-name: ${{ env.package-artifact-name }}
steps:
- name: 'Checkout ${{ github.base_ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -190,7 +190,7 @@ jobs:
dotnet-version: ${{ env.dotnet-sdk-version }}
- name: Download Build
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@v8
with:
name: build
@@ -199,7 +199,7 @@ jobs:
dotnet pack ${{ vars.SRC_DEFAULT_GLOB_PATTERN }} --configuration ${{ env.build-configuration }} /p:Platform="${{ env.build-platform }}" /p:PackageVersion=${{ env.release-version }} /p:Version=${{ env.assembly-version }} /p:AssemblyInformationalVersion=${{ env.assembly-informational-version }} /p:FileVersion=${{ env.file-version }} --output ${{ runner.temp }}/${{ env.nuget-packages-directory }}
- name: Upload Package
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ env.package-artifact-name }}
path: |
@@ -215,7 +215,7 @@ jobs:
environment: 'NuGet'
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -242,7 +242,7 @@ jobs:
notes-start-tag: ${{ needs.workflow-variables.outputs.notes-start-tag }}
steps:
- name: 'Checkout ${{ github.head_ref || github.ref }}'
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: 'Determine notes start tag'
id: determine-notes-start-tag
diff --git a/.gitignore b/.gitignore
index 88197d63..6105dc57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-## Ignore Visual Studio temporary files, build results, and
+## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
@@ -262,3 +262,6 @@ __pycache__/
# BenchmarkDotNet artifacts
**/BenchmarkDotNet.Artifacts/
+
+# GitHub Copilot Testing folder
+/.codetesting
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 00000000..cb8bb32a
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,93 @@
+# Polyline Algorithm Agents Instructions
+
+## Purpose
+
+Instructions for automated agents (bots, CI, and code review tools) and contributors interacting with the Polyline Algorithm library.
+
+---
+
+## General Guidelines
+
+- All contributions and automation **must adhere to code style** (`.editorconfig`, `dotnet format`).
+- **Unit tests** are required for new features and bug fixes (`tests/` directory).
+- **Benchmarks** must be updated for performance-impacting changes (`benchmarks/` directory).
+
+---
+
+## Pull Requests
+
+Agents and contributors should:
+
+- **Attach benchmark results** for encoding/decoding performance changes
+- Document **public API changes** in XML comments and verify updates at [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
+- Run format and static analysis tools before submitting (`dotnet format`, analyzers)
+- Update **README.md** and `/samples` for public API changes
+
+---
+
+## Error Handling and Logging
+
+- Throw **descriptive exceptions** for invalid input/edge cases
+- Use internal logging helpers for operational status (`LogInfoExtensions`, `LogWarningExtensions`)
+
+---
+
+## Encoding/Decoding Agents
+
+- Use abstraction interfaces (`IPolylineEncoder`, `IPolylineDecoder` if available)
+- Prefer extension methods for collections and arrays
+- Validate latitude/longitude ranges
+
+---
+
+## Issue and PR Templates
+
+Agents should reference standardized templates from `.github`. Contributors must use them for new issues or PRs.
+
+---
+
+## Extensibility
+
+- Add encoding schemes or coordinate types in **separate classes/files**
+- Register via factory pattern if supporting multiple algorithms
+- Do not mix logic between different polyline versions
+
+---
+
+## Future-proofing
+
+- Support for precision or custom coordinate fields: update `PolylineEncodingOptions` with clear doc comments
+
+---
+
+## Documentation
+
+- Keep XML doc comments up-to-date in source files
+- API reference is auto-generated and hosted at
+ [https://petesramek.github.io/polyline-algorithm-csharp/](https://petesramek.github.io/polyline-algorithm-csharp/)
+- After public API changes, verify docs render correctly on the website
+- Add usage samples in XML comments and `/samples` directory
+
+---
+
+## Agent File Format (for `.github/agents`)
+
+Each agent instruction file should specify:
+
+```
+# AGENT INSTRUCTIONS
+
+- Purpose and scope
+- Required tools/commands
+- Coding and testing requirements
+- Logging/error handling expectations
+- Documentation or samples to update
+```
+
+---
+
+## Contact & Questions
+
+Questions or clarifications: open a GitHub issue and tag `@petesramek`.
+
+---
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..274dcb87
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+# Contributing to PolylineAlgorithm
+
+Thank you for your interest in improving this library!
+
+## Guidelines
+
+- **Follow code style:** Use `.editorconfig` and run `dotnet format`.
+- **Add unit tests:** Place all tests in `/tests`, following naming conventions.
+- **Benchmark updates:** Add or update `/benchmarks` for major changes.
+
+## Issue and PR Templates
+
+Please use the provided templates in `.github` for all new issues or pull requests.
+
+## API Documentation
+
+API reference is auto-generated from XML comments and published at
+👉 [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
+
+- All public classes, interfaces, and methods require XML doc comments.
+- After merging, verify that documentation renders correctly.
+- Add usage samples where applicable.
+
+## Submitting a Change
+
+1. Fork the repo and create a new branch.
+2. Implement your changes, tests, and update doc comments.
+3. Run `dotnet format`, and all tests/benchmarks.
+4. Submit a pull request, using the provided template.
+
+## Contact
+
+For help or questions, open an issue and tag `@petesramek`.
+
+## License
+
+MIT License © Pete Sramek
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..96ebb124
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,25 @@
+
+
+
+ 14.0
+ enable
+ enable
+ true
+ en
+
+
+
+ latest
+ All
+ true
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/PolylineAlgorithm.slnx b/PolylineAlgorithm.slnx
index 5eac2683..29fa9445 100644
--- a/PolylineAlgorithm.slnx
+++ b/PolylineAlgorithm.slnx
@@ -1,4 +1,7 @@
+
+
+
diff --git a/README.md b/README.md
index 156a8f2a..b235ecaa 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,213 @@
-# PolylineAlgorithm for .NET
+# PolylineAlgorithm for .NET
-[](https://www.nuget.org/packages/PolylineAlgorithm/)
-[](https://opensource.org/licenses/MIT)
-[](https://github.com/sramekpete/polyline-algorithm-csharp/actions/workflows/build.yml)
+Lightweight .NET Standard 2.1 library implementing Google-compliant Encoded Polyline Algorithm with strong input validation, modern API patterns, and extensibility for custom coordinate types.
-Lightweight .NET Standard 2.1 library implementing Google Encoded Polyline Algorithm.
-Package should be primarily used as baseline for libraries that implement polyline encoding/decoding functionality.
+## Table of Contents
-More info about the algorithm can be found at [Google Developers](https://developers.google.com/maps/documentation/utilities/polylinealgorithm).
+- [Features](#features)
+- [Installation](#installation)
+- [Usage](#usage)
+- [API Reference](#api-reference)
+- [Benchmarks](#benchmarks)
+- [FAQ](#faq)
+- [Contributing](#contributing)
+- [Support](#support)
+- [License](#license)
-## Prerequisites
+## Features
-PolylineAlgorithm for .NET is available as a NuGet package targeting .NET Standard 2.1.
+- Fully compliant Google Encoded Polyline Algorithm for .NET Standard 2.1+
+- Immutable, strongly-typed coordinate and polyline data structures
+- Predefined encoder and decoder types for quick usage, extensibility for custom coordinate types
+- Robust input validation with descriptive exceptions for malformed/invalid data
+- Simple, extensible encoding and decoding APIs (`IPolylineEncoder`, `IPolylineDecoder`, `AbstractPolylineEncoder`, `AbstractPolylineDecoder`)
+- Default encoding and decoding implementations (`PolylineEncoder`, `PolylineDecoder`)
+- Advanced configuration via `PolylineEncodingOptions` (buffer size, logging, etc.)
+- Internal logging and diagnostic supports logging for CI/CD and developer diagnostics
+- Thorough unit tests and benchmarks for correctness and performance
+- Auto-generated API documentation ([API Reference](https://petesramek.github.io/polyline-algorithm-csharp/))
+- Support for .NET Core, .NET 5+, Xamarin, Unity, Blazor, and other platforms supporting `netstandard2.1`
-.NET CLI: `dotnet add package PolylineAlgorithm`
+## Installation
-Package Manager Console: `Install-Package PolylineAlgorithm`
+Using dotnet tool
-## How to use it
+```shell
+dotnet add package PolylineAlgorithm
+```
-In the majority of cases you would like to inherit `AbstractPolylineDecoder` and `AbstractPolylineEncoder` classes and implement abstract methods that are mainly responsible for extracting data from your coordinate and polyline types and creating new instances of them.
+or via NuGet
-In some cases you may want to implement your own decoder and encoder from scratch.
-In that case you can use `PolylineEncoding` static class that offers static methods for encoding and decoding polyline segments.
+```powershell
+Install-Package PolylineAlgorithm
+```
-## Documentation
+## Usage
-Documentation is can be found at [https://sramekpete.github.io/polyline-algorithm-csharp/](https://sramekpete.github.io/polyline-algorithm-csharp/).
\ No newline at end of file
+### PolylineEncoder and PolylineDecoder (predefined `Coordinate` and `Polyline` types)
+
+#### Encoding
+
+```csharp
+using PolylineAlgorithm;
+
+var coordinates = new List
+{
+ new Coordinate(48.858370, 2.294481),
+ new Coordinate(51.500729, -0.124625)
+};
+
+var encoder = new PolylineEncoder();
+Polyline encoded = encoder.Encode(coordinates);
+
+Console.WriteLine(encoded.ToString());
+```
+
+#### Decoding
+
+```csharp
+using PolylineAlgorithm;
+
+Polyline polyline = Polyline.FromString("yseiHoc_MwacOjnwM");
+
+var decoder = new PolylineDecoder();
+IEnumerable decoded = decoder.Decode(polyline);
+```
+
+### Custom encoder and decoder (user-defined coordinate and polyline types)
+
+#### Encoding
+
+Custom encoder implementation.
+
+```csharp
+public sealed class MyPolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> {
+ public PolylineEncoder()
+ : base() { }
+
+ public PolylineEncoder(PolylineEncodingOptions options)
+ : base(options) { }
+
+ protected override double GetLatitude((double Latitude, double Longitude) coordinate) {
+ return coordinate.Latitude;
+ }
+
+ protected override double GetLongitude((double Latitude, double Longitude) coordinate) {
+ return coordinate.Longitude;
+ }
+
+ protected override string CreatePolyline(ReadOnlyMemory polyline) {
+ return polyline.ToString();
+ }
+}
+```
+
+Custom encoder usage.
+
+```csharp
+using PolylineAlgorithm;
+
+var coordinates = new List<(double Latitude, double Longitude)>
+{
+ (48.858370, 2.294481),
+ (51.500729, -0.124625)
+};
+
+var encoder = new MyPolylineEncoder();
+string encoded = encoder.Encode(coordinates);
+
+Console.WriteLine(encoded.ToString());
+```
+
+#### Decoding
+
+Custom decoder implementation.
+
+```csharp
+public sealed class MyPolylineDecoder : AbstractPolylineDecoder {
+ public PolylineDecoder()
+ : base() { }
+
+ public PolylineDecoder(PolylineEncodingOptions options)
+ : base(options) { }
+
+ protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) {
+ return (latitude, longitude);
+ }
+
+ protected override ReadOnlyMemory GetReadOnlyMemory(ref string polyline) {
+ return polyline.AsMemory();
+ }
+}
+```
+
+Custom decoder usage.
+
+```csharp
+using PolylineAlgorithm;
+
+string encoded = "yseiHoc_MwacOjnwM";
+
+var decoder = new MyPolylineDecoder();
+IEnumerable<(double Latitude, double Longitude)> decoded = decoder.Decode(encoded);
+```
+
+> **Note:**
+> If you need low-level utilities for normalization, validation, encoding and decoding, use static methods from the `PolylineEncoding` class.
+
+## API Reference
+
+Full API docs and guides (auto-generated from source) are available at [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
+
+## Benchmarks
+
+- See `/benchmarks` in the repo for performance evaluation.
+- Contributors: Update benchmarks and document results for performance-impacting PRs.
+
+## FAQ
+
+**Q: What coordinate ranges are valid?**
+A: Latitude must be -90..90; longitude -180..180. Out-of-range input throws `ArgumentOutOfRangeException`.
+
+**Q: Which .NET versions are supported?**
+A: All platforms supporting `netstandard2.1` (including .NET Core and .NET 5+).
+
+**Q: What happens if I pass invalid or malformed input to the decoder?**
+A: The decoder will throw descriptive exceptions for malformed polyline strings. Check exception handling in your application.
+
+**Q: How do I customize encoding options (e.g., buffer size, logging)?**
+A: Use the PolylineEncodingOptionsBuilder to set custom options and pass to the PolylineEncoder constructor.
+
+**Q: Is the library thread-safe?**
+A: Yes, the main encoding and decoding APIs are stateless and thread-safe. If using mutable shared resources, manage synchronization in your code.
+
+**Q: Can the library be used in Unity, Xamarin, Blazor, or other .NET-compatible platforms?**
+A: Yes! Any environment supporting netstandard2.1 can use this library.
+
+**Q: Where can I report bugs or request features?**
+A: Open a GitHub issue using the provided templates in the repository and tag @petesramek.
+
+**Q: Is there support for elevation, time stamps, or third coordinate values?**
+A: Not currently, not planned to be added, but you can extend by implementing your own encoder/decoder using `PolylineEncoding` class methods.
+
+**Q: How do I contribute documentation improvements?**
+A: Update XML doc comments in the codebase and submit a PR; all public APIs require XML documentation. In case, you would like to improve guides you have to updage relevant markdown file in `/api-reference/guide` folder.
+
+**Q: Does the library support streaming or incremental decoding of polylines?**
+A: Currently, only batch encode/decode is supported. For streaming scenarios, implement custom logic using `PolylineEncoding` utility functions.
+
+## Contributing
+
+- Follow code style and PR instructions in [AGENTS.md](./AGENTS.md).
+- Ensure all features are covered by tests and XML doc comments.
+- For questions or suggestions, open an issue and use the provided templates.
+
+## Support
+
+Have a question, bug, or feature request? [Open an issue!](https://github.com/petesramek/polyline-algorithm-csharp/issues)
+
+---
+
+## License
+
+MIT License © Pete Sramek
diff --git a/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml b/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml
index bffca6fe..9f220428 100644
--- a/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml
@@ -3,7 +3,7 @@ title: Class AbstractPolylineDecoder
body:
- api1: Class AbstractPolylineDecoder
id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L23
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L21
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2
commentId: T:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2
@@ -54,7 +54,7 @@ body:
- h2: Constructors
- api3: AbstractPolylineDecoder()
id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L27
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L27
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor
@@ -62,7 +62,7 @@ body:
- code: protected AbstractPolylineDecoder()
- api3: AbstractPolylineDecoder(PolylineEncodingOptions)
id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor_PolylineAlgorithm_PolylineEncodingOptions_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L39
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L39
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
@@ -84,11 +84,11 @@ body:
- h2: Properties
- api3: Options
id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Options
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L46
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L53
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Options
commentId: P:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Options
-- markdown: Gets the encoding options used by this polyline encoder.
+- markdown: Gets the encoding options used by this polyline decoder.
- code: public PolylineEncodingOptions Options { get; }
- h4: Property Value
- parameters:
@@ -98,11 +98,10 @@ body:
- h2: Methods
- api3: CreateCoordinate(double, double)
id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_CreateCoordinate_System_Double_System_Double_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L153
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L180
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.CreateCoordinate(System.Double,System.Double)
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.CreateCoordinate(System.Double,System.Double)
-- markdown: Creates a coordinate instance from the given latitude and longitude values.
- code: protected abstract TCoordinate CreateCoordinate(double latitude, double longitude)
- h4: Parameters
- parameters:
@@ -110,20 +109,17 @@ body:
type:
- text: double
url: https://learn.microsoft.com/dotnet/api/system.double
- description: The latitude value.
- name: longitude
type:
- text: double
url: https://learn.microsoft.com/dotnet/api/system.double
- description: The longitude value.
- h4: Returns
- parameters:
- type:
- TCoordinate
- description: A coordinate instance of type TCoordinate.
- api3: Decode(TPolyline)
id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Decode__0_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L66
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L73
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0)
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0)
@@ -156,22 +152,62 @@ body:
description: Thrown when polyline is empty.
- type:
- text: InvalidPolylineException
- url: PolylineAlgorithm.InvalidPolylineException.html
+ url: PolylineAlgorithm.Diagnostics.InvalidPolylineException.html
description: Thrown when the polyline format is invalid or malformed at a specific position.
-- api3: GetReadOnlyMemory(TPolyline)
- id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_GetReadOnlyMemory__0_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L139
+- api3: Decode(TPolyline, CancellationToken)
+ id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Decode__0_System_Threading_CancellationToken_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L85
metadata:
- uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0)
- commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0)
-- markdown: Converts the provided polyline instance into a for decoding.
-- code: protected abstract ReadOnlyMemory GetReadOnlyMemory(TPolyline polyline)
+ uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken)
+ commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken)
+- markdown: Decodes an encoded polyline with cancellation support.
+- code: public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - TPolyline
+ description: The encoded polyline.
+ - name: cancellationToken
+ type:
+ - text: CancellationToken
+ url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken
+ description: Cancellation token.
+- h4: Returns
+- parameters:
+ - type:
+ - text: IEnumerable
+ url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1
+ - <
+ - TCoordinate
+ - '>'
+ description: Decoded coordinates.
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentNullException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception
+ description: ''
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: ''
+ - type:
+ - text: InvalidPolylineException
+ url: PolylineAlgorithm.Diagnostics.InvalidPolylineException.html
+ description: ''
+- api3: GetReadOnlyMemory(in TPolyline)
+ id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_GetReadOnlyMemory__0__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L177
+ metadata:
+ uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0@)
+ commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0@)
+- code: protected abstract ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline)
- h4: Parameters
- parameters:
- name: polyline
type:
- TPolyline
- description: The TPolyline instance containing the encoded polyline data to decode.
- h4: Returns
- parameters:
- type:
@@ -181,7 +217,29 @@ body:
- text: char
url: https://learn.microsoft.com/dotnet/api/system.char
- '>'
- description: A representing the encoded polyline data.
+- api3: ValidateFormat(ReadOnlyMemory, ILogger?)
+ id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_ValidateFormat_System_ReadOnlyMemory_System_Char__Microsoft_Extensions_Logging_ILogger_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L166
+ metadata:
+ uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.ValidateFormat(System.ReadOnlyMemory{System.Char},Microsoft.Extensions.Logging.ILogger)
+ commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.ValidateFormat(System.ReadOnlyMemory{System.Char},Microsoft.Extensions.Logging.ILogger)
+- markdown: Validates the polyline format for allowed characters.
+- code: protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? logger)
+- h4: Parameters
+- parameters:
+ - name: sequence
+ type:
+ - text: ReadOnlyMemory
+ url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ - name: logger
+ type:
+ - text: ILogger
+ url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger
+ - '?'
languageId: csharp
metadata:
description: >-
diff --git a/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml b/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml
index 45ae4d3e..da8b0825 100644
--- a/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml
@@ -3,7 +3,7 @@ title: Class AbstractPolylineEncoder
body:
- api1: Class AbstractPolylineEncoder
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L25
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L25
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2
commentId: T:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2
@@ -49,12 +49,18 @@ body:
url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
- text: object.ToString()
url: https://learn.microsoft.com/dotnet/api/system.object.tostring
+- h4: Extension Methods
+- list:
+ - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, List)
+ url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__
+ - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, TCoordinate[])
+ url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___
- h2: Remarks
- markdown: This abstract class serves as a base for specific polyline encoders, allowing customization of the encoding process.
- h2: Constructors
- api3: AbstractPolylineEncoder()
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L29
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L30
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor
@@ -62,7 +68,7 @@ body:
- code: protected AbstractPolylineEncoder()
- api3: AbstractPolylineEncoder(PolylineEncodingOptions)
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor_PolylineAlgorithm_PolylineEncodingOptions_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L39
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L40
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
@@ -84,7 +90,7 @@ body:
- h2: Properties
- api3: Options
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Options
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L46
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L54
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Options
commentId: P:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Options
@@ -98,7 +104,7 @@ body:
- h2: Methods
- api3: CreatePolyline(ReadOnlyMemory)
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_CreatePolyline_System_ReadOnlyMemory_System_Char__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L199
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L162
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.CreatePolyline(System.ReadOnlyMemory{System.Char})
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.CreatePolyline(System.ReadOnlyMemory{System.Char})
@@ -120,20 +126,23 @@ body:
- type:
- TPolyline
description: An instance of TPolyline representing the encoded polyline.
-- api3: Encode(IEnumerable)
- id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Encode_System_Collections_Generic_IEnumerable__0__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L64
+- api3: Encode(ReadOnlySpan)
+ id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Encode_System_ReadOnlySpan__0__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L73
metadata:
- uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.Collections.Generic.IEnumerable{`0})
- commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.Collections.Generic.IEnumerable{`0})
+ uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.ReadOnlySpan{`0})
+ commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.ReadOnlySpan{`0})
- markdown: Encodes a collection of TCoordinate instances into an encoded TPolyline string.
-- code: public TPolyline Encode(IEnumerable coordinates)
+- code: >-
+ [SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains local methods. Actual method only 55 lines.")]
+
+ public TPolyline Encode(ReadOnlySpan coordinates)
- h4: Parameters
- parameters:
- name: coordinates
type:
- - text: IEnumerable
- url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
- <
- TCoordinate
- '>'
@@ -142,10 +151,7 @@ body:
- parameters:
- type:
- TPolyline
- description: >-
- An instance of TPolyline representing the encoded coordinates.
-
- Returns default if the input collection is empty or null.
+ description: An instance of TPolyline representing the encoded coordinates.
- h4: Exceptions
- parameters:
- type:
@@ -156,9 +162,17 @@ body:
- text: ArgumentException
url: https://learn.microsoft.com/dotnet/api/system.argumentexception
description: Thrown when coordinates is an empty enumeration.
+ - type:
+ - text: InternalBufferOverflowException
+ url: https://learn.microsoft.com/dotnet/api/system.io.internalbufferoverflowexception
+ description: ''
+ - type:
+ - text: InvalidOperationException
+ url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception
+ description: ''
- api3: GetLatitude(TCoordinate)
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_GetLatitude__0_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L219
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L182
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLatitude(`0)
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLatitude(`0)
@@ -178,7 +192,7 @@ body:
description: The latitude value as a .
- api3: GetLongitude(TCoordinate)
id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_GetLongitude__0_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L209
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L172
metadata:
uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLongitude(`0)
commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLongitude(`0)
diff --git a/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml b/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml
index 01478f20..aff84ab1 100644
--- a/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml
@@ -3,7 +3,7 @@ title: Interface IPolylineDecoder
body:
- api1: Interface IPolylineDecoder
id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L13
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L13
metadata:
uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2
commentId: T:PolylineAlgorithm.Abstraction.IPolylineDecoder`2
@@ -15,7 +15,7 @@ body:
- name: Assembly
value: PolylineAlgorithm.dll
- markdown: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates.
-- code: public interface IPolylineDecoder
+- code: public interface IPolylineDecoder
- h4: Type Parameters
- parameters:
- name: TPolyline
@@ -23,12 +23,12 @@ body:
- h2: Methods
- api3: Decode(TPolyline)
id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2_Decode__0_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L23
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L23
metadata:
uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0)
commentId: M:PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0)
- markdown: Decodes the specified encoded polyline into a sequence of geographic coordinates.
-- code: IEnumerable Decode(TPolyline polyline)
+- code: IEnumerable Decode(TPolyline polyline)
- h4: Parameters
- parameters:
- name: polyline
diff --git a/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml b/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml
index 424f9516..cd418379 100644
--- a/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml
@@ -3,7 +3,7 @@ title: Interface IPolylineEncoder
body:
- api1: Interface IPolylineEncoder
id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L13
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L10
metadata:
uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2
commentId: T:PolylineAlgorithm.Abstraction.IPolylineEncoder`2
@@ -20,21 +20,27 @@ body:
- parameters:
- name: TCoordinate
- name: TPolyline
+- h4: Extension Methods
+- list:
+ - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, List)
+ url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__
+ - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, TCoordinate[])
+ url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___
- h2: Methods
-- api3: Encode(IEnumerable)
- id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2_Encode_System_Collections_Generic_IEnumerable__0__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L23
+- api3: Encode(ReadOnlySpan)
+ id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2_Encode_System_ReadOnlySpan__0__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L20
metadata:
- uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.Collections.Generic.IEnumerable{`0})
- commentId: M:PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.Collections.Generic.IEnumerable{`0})
+ uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0})
+ commentId: M:PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0})
- markdown: Encodes a sequence of geographic coordinates into an encoded polyline representation.
-- code: TPolyline Encode(IEnumerable coordinates)
+- code: TPolyline Encode(ReadOnlySpan coordinates)
- h4: Parameters
- parameters:
- name: coordinates
type:
- - text: IEnumerable
- url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
- <
- TCoordinate
- '>'
diff --git a/api-reference/1.0/PolylineAlgorithm.Coordinate.Validator.yml b/api-reference/1.0/PolylineAlgorithm.Coordinate.Validator.yml
new file mode 100644
index 00000000..58a7d4d0
--- /dev/null
+++ b/api-reference/1.0/PolylineAlgorithm.Coordinate.Validator.yml
@@ -0,0 +1,131 @@
+### YamlMime:ApiPage
+title: Class Coordinate.Validator
+body:
+- api1: Class Coordinate.Validator
+ id: PolylineAlgorithm_Coordinate_Validator
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L151
+ metadata:
+ uid: PolylineAlgorithm.Coordinate.Validator
+ commentId: T:PolylineAlgorithm.Coordinate.Validator
+- facts:
+ - name: Namespace
+ value:
+ text: PolylineAlgorithm
+ url: PolylineAlgorithm.html
+ - name: Assembly
+ value: PolylineAlgorithm.dll
+- markdown: Provides static methods for validating latitude and longitude values used in .
+- code: public static class Coordinate.Validator
+- h4: Inheritance
+- inheritance:
+ - text: object
+ url: https://learn.microsoft.com/dotnet/api/system.object
+ - text: Coordinate.Validator
+ url: PolylineAlgorithm.Coordinate.Validator.html
+- h4: Inherited Members
+- list:
+ - text: object.Equals(object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
+ - text: object.Equals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)
+ - text: object.GetHashCode()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode
+ - text: object.GetType()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gettype
+ - text: object.MemberwiseClone()
+ url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone
+ - text: object.ReferenceEquals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
+ - text: object.ToString()
+ url: https://learn.microsoft.com/dotnet/api/system.object.tostring
+- h2: Remarks
+- markdown: >-
+ The Validator class ensures that latitude and longitude values are within their valid ranges:
+
+
+ Latitude must be between -90 and 90 degrees. Longitude must be between -180 and 180 degrees.
+
+
+ If a value is out of range or not finite, an exception is thrown via .
+- h2: Methods
+- api3: ValidateLatitude(double)
+ id: PolylineAlgorithm_Coordinate_Validator_ValidateLatitude_System_Double_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L159
+ metadata:
+ uid: PolylineAlgorithm.Coordinate.Validator.ValidateLatitude(System.Double)
+ commentId: M:PolylineAlgorithm.Coordinate.Validator.ValidateLatitude(System.Double)
+- markdown: Validates that the specified latitude is within the valid range of -90 to 90 degrees.
+- code: public static void ValidateLatitude(double latitude)
+- h4: Parameters
+- parameters:
+ - name: latitude
+ type:
+ - text: double
+ url: https://learn.microsoft.com/dotnet/api/system.double
+ description: The latitude value to validate.
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentOutOfRangeException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception
+ description: Thrown if latitude is less than -90, greater than 90, or not a finite number.
+- api3: ValidateLongitude(double)
+ id: PolylineAlgorithm_Coordinate_Validator_ValidateLongitude_System_Double_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L174
+ metadata:
+ uid: PolylineAlgorithm.Coordinate.Validator.ValidateLongitude(System.Double)
+ commentId: M:PolylineAlgorithm.Coordinate.Validator.ValidateLongitude(System.Double)
+- markdown: Validates that the specified longitude is within the valid range of -180 to 180 degrees.
+- code: public static void ValidateLongitude(double longitude)
+- h4: Parameters
+- parameters:
+ - name: longitude
+ type:
+ - text: double
+ url: https://learn.microsoft.com/dotnet/api/system.double
+ description: The longitude value to validate.
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentOutOfRangeException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception
+ description: Thrown if longitude is less than -180, greater than 180, or not a finite number.
+- api3: ValidateValue(double, double, double, string)
+ id: PolylineAlgorithm_Coordinate_Validator_ValidateValue_System_Double_System_Double_System_Double_System_String_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L192
+ metadata:
+ uid: PolylineAlgorithm.Coordinate.Validator.ValidateValue(System.Double,System.Double,System.Double,System.String)
+ commentId: M:PolylineAlgorithm.Coordinate.Validator.ValidateValue(System.Double,System.Double,System.Double,System.String)
+- markdown: Validates that the specified value is finite and within the specified range.
+- code: public static void ValidateValue(double value, double min, double max, string paramName)
+- h4: Parameters
+- parameters:
+ - name: value
+ type:
+ - text: double
+ url: https://learn.microsoft.com/dotnet/api/system.double
+ description: The value to validate.
+ - name: min
+ type:
+ - text: double
+ url: https://learn.microsoft.com/dotnet/api/system.double
+ description: The minimum allowed value (inclusive).
+ - name: max
+ type:
+ - text: double
+ url: https://learn.microsoft.com/dotnet/api/system.double
+ description: The maximum allowed value (inclusive).
+ - name: paramName
+ type:
+ - text: string
+ url: https://learn.microsoft.com/dotnet/api/system.string
+ description: The name of the parameter being validated.
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentOutOfRangeException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception
+ description: Thrown if value is not finite, less than min, or greater than max.
+languageId: csharp
+metadata:
+ description: Provides static methods for validating latitude and longitude values used in .
diff --git a/api-reference/1.0/PolylineAlgorithm.Coordinate.yml b/api-reference/1.0/PolylineAlgorithm.Coordinate.yml
index 7f5afdf3..27035645 100644
--- a/api-reference/1.0/PolylineAlgorithm.Coordinate.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Coordinate.yml
@@ -3,7 +3,7 @@ title: Struct Coordinate
body:
- api1: Struct Coordinate
id: PolylineAlgorithm_Coordinate
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L22
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L25
metadata:
uid: PolylineAlgorithm.Coordinate
commentId: T:PolylineAlgorithm.Coordinate
@@ -41,18 +41,23 @@ body:
geographic coordinates in degrees. It includes validation for latitude and longitude ranges
and provides methods for equality comparison and string representation.
+
+
+ Note: The value (0, 0) is a valid coordinate (Gulf of Guinea), but is also treated as the "default" value by .
- h2: Constructors
- api3: Coordinate()
id: PolylineAlgorithm_Coordinate__ctor
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L28
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L34
metadata:
uid: PolylineAlgorithm.Coordinate.#ctor
commentId: M:PolylineAlgorithm.Coordinate.#ctor
- markdown: Initializes a new instance of the struct with default values (0) for and .
- code: public Coordinate()
+- h4: Remarks
+- markdown: The default value (0, 0) is a valid coordinate (Gulf of Guinea), but is also treated as the "default" value by .
- api3: Coordinate(double, double)
id: PolylineAlgorithm_Coordinate__ctor_System_Double_System_Double_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L46
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L52
metadata:
uid: PolylineAlgorithm.Coordinate.#ctor(System.Double,System.Double)
commentId: M:PolylineAlgorithm.Coordinate.#ctor(System.Double,System.Double)
@@ -82,7 +87,7 @@ body:
- h2: Properties
- api3: Latitude
id: PolylineAlgorithm_Coordinate_Latitude
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L62
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L62
metadata:
uid: PolylineAlgorithm.Coordinate.Latitude
commentId: P:PolylineAlgorithm.Coordinate.Latitude
@@ -95,7 +100,7 @@ body:
url: https://learn.microsoft.com/dotnet/api/system.double
- api3: Longitude
id: PolylineAlgorithm_Coordinate_Longitude
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L67
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L67
metadata:
uid: PolylineAlgorithm.Coordinate.Longitude
commentId: P:PolylineAlgorithm.Coordinate.Longitude
@@ -109,7 +114,7 @@ body:
- h2: Methods
- api3: Equals(object?)
id: PolylineAlgorithm_Coordinate_Equals_System_Object_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L82
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L83
metadata:
uid: PolylineAlgorithm.Coordinate.Equals(System.Object)
commentId: M:PolylineAlgorithm.Coordinate.Equals(System.Object)
@@ -131,7 +136,7 @@ body:
description: true if obj and this instance are the same type and represent the same value; otherwise, false .
- api3: Equals(Coordinate)
id: PolylineAlgorithm_Coordinate_Equals_PolylineAlgorithm_Coordinate_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L102
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L86
metadata:
uid: PolylineAlgorithm.Coordinate.Equals(PolylineAlgorithm.Coordinate)
commentId: M:PolylineAlgorithm.Coordinate.Equals(PolylineAlgorithm.Coordinate)
@@ -150,9 +155,35 @@ body:
- text: bool
url: https://learn.microsoft.com/dotnet/api/system.boolean
description: true if the current object is equal to the other parameter; otherwise, false .
+- api3: Equals(Coordinate, double)
+ id: PolylineAlgorithm_Coordinate_Equals_PolylineAlgorithm_Coordinate_System_Double_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L99
+ metadata:
+ uid: PolylineAlgorithm.Coordinate.Equals(PolylineAlgorithm.Coordinate,System.Double)
+ commentId: M:PolylineAlgorithm.Coordinate.Equals(PolylineAlgorithm.Coordinate,System.Double)
+- markdown: Determines whether two instances are approximately equal within a specified tolerance.
+- code: public bool Equals(Coordinate other, double tolerance)
+- h4: Parameters
+- parameters:
+ - name: other
+ type:
+ - text: Coordinate
+ url: PolylineAlgorithm.Coordinate.html
+ description: The other coordinate to compare.
+ - name: tolerance
+ type:
+ - text: double
+ url: https://learn.microsoft.com/dotnet/api/system.double
+ description: The maximum allowed difference for latitude and longitude.
+- h4: Returns
+- parameters:
+ - type:
+ - text: bool
+ url: https://learn.microsoft.com/dotnet/api/system.boolean
+ description: true if both latitude and longitude differ by less than tolerance; otherwise, false .
- api3: GetHashCode()
id: PolylineAlgorithm_Coordinate_GetHashCode
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L85
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L104
metadata:
uid: PolylineAlgorithm.Coordinate.GetHashCode
commentId: M:PolylineAlgorithm.Coordinate.GetHashCode
@@ -166,7 +197,7 @@ body:
description: A 32-bit signed integer that is the hash code for this instance.
- api3: IsDefault()
id: PolylineAlgorithm_Coordinate_IsDefault
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L75
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L78
metadata:
uid: PolylineAlgorithm.Coordinate.IsDefault
commentId: M:PolylineAlgorithm.Coordinate.IsDefault
@@ -178,9 +209,11 @@ body:
- text: bool
url: https://learn.microsoft.com/dotnet/api/system.boolean
description: true if both latitude and longitude are 0; otherwise, false .
+- h4: Remarks
+- markdown: The value (0, 0) is a valid coordinate (Gulf of Guinea), but is also treated as the "default" value by this method.
- api3: ToString()
id: PolylineAlgorithm_Coordinate_ToString
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L93
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L112
metadata:
uid: PolylineAlgorithm.Coordinate.ToString
commentId: M:PolylineAlgorithm.Coordinate.ToString
@@ -195,7 +228,7 @@ body:
- h2: Operators
- api3: operator ==(Coordinate, Coordinate)
id: PolylineAlgorithm_Coordinate_op_Equality_PolylineAlgorithm_Coordinate_PolylineAlgorithm_Coordinate_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L119
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L124
metadata:
uid: PolylineAlgorithm.Coordinate.op_Equality(PolylineAlgorithm.Coordinate,PolylineAlgorithm.Coordinate)
commentId: M:PolylineAlgorithm.Coordinate.op_Equality(PolylineAlgorithm.Coordinate,PolylineAlgorithm.Coordinate)
@@ -221,7 +254,7 @@ body:
description: true if both coordinates are equal; otherwise, false .
- api3: operator !=(Coordinate, Coordinate)
id: PolylineAlgorithm_Coordinate_op_Inequality_PolylineAlgorithm_Coordinate_PolylineAlgorithm_Coordinate_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Coordinate.cs#L129
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Coordinate.cs#L134
metadata:
uid: PolylineAlgorithm.Coordinate.op_Inequality(PolylineAlgorithm.Coordinate,PolylineAlgorithm.Coordinate)
commentId: M:PolylineAlgorithm.Coordinate.op_Inequality(PolylineAlgorithm.Coordinate,PolylineAlgorithm.Coordinate)
diff --git a/api-reference/1.0/PolylineAlgorithm.CoordinateValueType.yml b/api-reference/1.0/PolylineAlgorithm.CoordinateValueType.yml
index 775098b4..915d1643 100644
--- a/api-reference/1.0/PolylineAlgorithm.CoordinateValueType.yml
+++ b/api-reference/1.0/PolylineAlgorithm.CoordinateValueType.yml
@@ -3,7 +3,7 @@ title: Enum CoordinateValueType
body:
- api1: Enum CoordinateValueType
id: PolylineAlgorithm_CoordinateValueType
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/CoordinateValueType.cs#L11
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/CoordinateValueType.cs#L11
metadata:
uid: PolylineAlgorithm.CoordinateValueType
commentId: T:PolylineAlgorithm.CoordinateValueType
@@ -28,7 +28,7 @@ body:
description: >+
Represents a longitude value.
- - name: None
+ - name: Unspecified
default: "0"
description: >+
Represents no specific type. This value is used when the type is not applicable or not specified.
diff --git a/api-reference/1.0/PolylineAlgorithm.Diagnostics.InvalidPolylineException.yml b/api-reference/1.0/PolylineAlgorithm.Diagnostics.InvalidPolylineException.yml
new file mode 100644
index 00000000..585ee5a2
--- /dev/null
+++ b/api-reference/1.0/PolylineAlgorithm.Diagnostics.InvalidPolylineException.yml
@@ -0,0 +1,102 @@
+### YamlMime:ApiPage
+title: Class InvalidPolylineException
+body:
+- api1: Class InvalidPolylineException
+ id: PolylineAlgorithm_Diagnostics_InvalidPolylineException
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Diagnostics/InvalidPolylineException.cs#L17
+ metadata:
+ uid: PolylineAlgorithm.Diagnostics.InvalidPolylineException
+ commentId: T:PolylineAlgorithm.Diagnostics.InvalidPolylineException
+- facts:
+ - name: Namespace
+ value:
+ text: PolylineAlgorithm.Diagnostics
+ url: PolylineAlgorithm.Diagnostics.html
+ - name: Assembly
+ value: PolylineAlgorithm.dll
+- markdown: Exception thrown when a polyline is determined to be malformed or invalid during processing.
+- code: 'public sealed class InvalidPolylineException : Exception, ISerializable'
+- h4: Inheritance
+- inheritance:
+ - text: object
+ url: https://learn.microsoft.com/dotnet/api/system.object
+ - text: Exception
+ url: https://learn.microsoft.com/dotnet/api/system.exception
+ - text: InvalidPolylineException
+ url: PolylineAlgorithm.Diagnostics.InvalidPolylineException.html
+- h4: Implements
+- list:
+ - text: ISerializable
+ url: https://learn.microsoft.com/dotnet/api/system.runtime.serialization.iserializable
+- h4: Inherited Members
+- list:
+ - text: Exception.GetBaseException()
+ url: https://learn.microsoft.com/dotnet/api/system.exception.getbaseexception
+ - text: Exception.GetObjectData(SerializationInfo, StreamingContext)
+ url: https://learn.microsoft.com/dotnet/api/system.exception.getobjectdata
+ - text: Exception.GetType()
+ url: https://learn.microsoft.com/dotnet/api/system.exception.gettype
+ - text: Exception.ToString()
+ url: https://learn.microsoft.com/dotnet/api/system.exception.tostring
+ - text: Exception.Data
+ url: https://learn.microsoft.com/dotnet/api/system.exception.data
+ - text: Exception.HelpLink
+ url: https://learn.microsoft.com/dotnet/api/system.exception.helplink
+ - text: Exception.HResult
+ url: https://learn.microsoft.com/dotnet/api/system.exception.hresult
+ - text: Exception.InnerException
+ url: https://learn.microsoft.com/dotnet/api/system.exception.innerexception
+ - text: Exception.Message
+ url: https://learn.microsoft.com/dotnet/api/system.exception.message
+ - text: Exception.Source
+ url: https://learn.microsoft.com/dotnet/api/system.exception.source
+ - text: Exception.StackTrace
+ url: https://learn.microsoft.com/dotnet/api/system.exception.stacktrace
+ - text: Exception.TargetSite
+ url: https://learn.microsoft.com/dotnet/api/system.exception.targetsite
+ - text: object.Equals(object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
+ - text: object.Equals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)
+ - text: object.GetHashCode()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode
+ - text: object.GetType()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gettype
+ - text: object.ReferenceEquals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
+ - text: object.ToString()
+ url: https://learn.microsoft.com/dotnet/api/system.object.tostring
+- h2: Remarks
+- markdown: This exception is used internally to indicate that a polyline string does not conform to the expected format or contains errors.
+- h2: Constructors
+- api3: InvalidPolylineException()
+ id: PolylineAlgorithm_Diagnostics_InvalidPolylineException__ctor
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Diagnostics/InvalidPolylineException.cs#L22
+ metadata:
+ uid: PolylineAlgorithm.Diagnostics.InvalidPolylineException.#ctor
+ commentId: M:PolylineAlgorithm.Diagnostics.InvalidPolylineException.#ctor
+- markdown: Initializes a new instance of the class.
+- code: public InvalidPolylineException()
+- api3: InvalidPolylineException(string, Exception)
+ id: PolylineAlgorithm_Diagnostics_InvalidPolylineException__ctor_System_String_System_Exception_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Diagnostics/InvalidPolylineException.cs#L43
+ metadata:
+ uid: PolylineAlgorithm.Diagnostics.InvalidPolylineException.#ctor(System.String,System.Exception)
+ commentId: M:PolylineAlgorithm.Diagnostics.InvalidPolylineException.#ctor(System.String,System.Exception)
+- markdown: Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception.
+- code: public InvalidPolylineException(string message, Exception innerException)
+- h4: Parameters
+- parameters:
+ - name: message
+ type:
+ - text: string
+ url: https://learn.microsoft.com/dotnet/api/system.string
+ description: The error message that explains the reason for the exception.
+ - name: innerException
+ type:
+ - text: Exception
+ url: https://learn.microsoft.com/dotnet/api/system.exception
+ description: The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
+languageId: csharp
+metadata:
+ description: Exception thrown when a polyline is determined to be malformed or invalid during processing.
diff --git a/api-reference/1.0/PolylineAlgorithm.Diagnostics.yml b/api-reference/1.0/PolylineAlgorithm.Diagnostics.yml
new file mode 100644
index 00000000..6fba5591
--- /dev/null
+++ b/api-reference/1.0/PolylineAlgorithm.Diagnostics.yml
@@ -0,0 +1,15 @@
+### YamlMime:ApiPage
+title: Namespace PolylineAlgorithm.Diagnostics
+body:
+- api1: Namespace PolylineAlgorithm.Diagnostics
+ id: PolylineAlgorithm_Diagnostics
+ metadata:
+ uid: PolylineAlgorithm.Diagnostics
+ commentId: N:PolylineAlgorithm.Diagnostics
+- h3: Classes
+- parameters:
+ - type:
+ text: InvalidPolylineException
+ url: PolylineAlgorithm.Diagnostics.InvalidPolylineException.html
+ description: Exception thrown when a polyline is determined to be malformed or invalid during processing.
+languageId: csharp
diff --git a/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml b/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml
index 086af359..7279835d 100644
--- a/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml
@@ -3,7 +3,7 @@ title: Class PolylineDecoderExtensions
body:
- api1: Class PolylineDecoderExtensions
id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L16
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L17
metadata:
uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions
commentId: T:PolylineAlgorithm.Extensions.PolylineDecoderExtensions
@@ -41,7 +41,7 @@ body:
- h2: Methods
- api3: Decode(IPolylineDecoder, string)
id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode_PolylineAlgorithm_Abstraction_IPolylineDecoder_PolylineAlgorithm_Polyline_PolylineAlgorithm_Coordinate__System_String_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L32
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L33
metadata:
uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(PolylineAlgorithm.Abstraction.IPolylineDecoder{PolylineAlgorithm.Polyline,PolylineAlgorithm.Coordinate},System.String)
commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(PolylineAlgorithm.Abstraction.IPolylineDecoder{PolylineAlgorithm.Polyline,PolylineAlgorithm.Coordinate},System.String)
@@ -85,7 +85,7 @@ body:
description: Thrown when decoder is null .
- api3: Decode(IPolylineDecoder, char[])
id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode_PolylineAlgorithm_Abstraction_IPolylineDecoder_PolylineAlgorithm_Polyline_PolylineAlgorithm_Coordinate__System_Char___
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L55
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L56
metadata:
uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(PolylineAlgorithm.Abstraction.IPolylineDecoder{PolylineAlgorithm.Polyline,PolylineAlgorithm.Coordinate},System.Char[])
commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(PolylineAlgorithm.Abstraction.IPolylineDecoder{PolylineAlgorithm.Polyline,PolylineAlgorithm.Coordinate},System.Char[])
@@ -131,7 +131,7 @@ body:
description: Thrown when decoder is null .
- api3: Decode(IPolylineDecoder, ReadOnlyMemory)
id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode_PolylineAlgorithm_Abstraction_IPolylineDecoder_PolylineAlgorithm_Polyline_PolylineAlgorithm_Coordinate__System_ReadOnlyMemory_System_Char__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L78
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L79
metadata:
uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(PolylineAlgorithm.Abstraction.IPolylineDecoder{PolylineAlgorithm.Polyline,PolylineAlgorithm.Coordinate},System.ReadOnlyMemory{System.Char})
commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(PolylineAlgorithm.Abstraction.IPolylineDecoder{PolylineAlgorithm.Polyline,PolylineAlgorithm.Coordinate},System.ReadOnlyMemory{System.Char})
diff --git a/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml b/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml
index 767322e4..5b05f48a 100644
--- a/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml
@@ -3,7 +3,7 @@ title: Class PolylineEncoderExtensions
body:
- api1: Class PolylineEncoderExtensions
id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L16
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L16
metadata:
uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions
commentId: T:PolylineAlgorithm.Extensions.PolylineEncoderExtensions
@@ -39,14 +39,19 @@ body:
- text: object.ToString()
url: https://learn.microsoft.com/dotnet/api/system.object.tostring
- h2: Methods
-- api3: Encode(IPolylineEncoder, ICollection)
- id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode_PolylineAlgorithm_Abstraction_IPolylineEncoder_PolylineAlgorithm_Coordinate_PolylineAlgorithm_Polyline__System_Collections_Generic_ICollection_PolylineAlgorithm_Coordinate__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L32
+- api3: Encode(IPolylineEncoder, List)
+ id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L32
metadata:
- uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(PolylineAlgorithm.Abstraction.IPolylineEncoder{PolylineAlgorithm.Coordinate,PolylineAlgorithm.Polyline},System.Collections.Generic.ICollection{PolylineAlgorithm.Coordinate})
- commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(PolylineAlgorithm.Abstraction.IPolylineEncoder{PolylineAlgorithm.Coordinate,PolylineAlgorithm.Polyline},System.Collections.Generic.ICollection{PolylineAlgorithm.Coordinate})
+ uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},System.Collections.Generic.List{``0})
+ commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},System.Collections.Generic.List{``0})
- markdown: Encodes a collection of instances into an encoded polyline.
-- code: public static Polyline Encode(this IPolylineEncoder encoder, ICollection coordinates)
+- code: >-
+ [SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "We need a list as we do need to marshal it as span.")]
+
+ [SuppressMessage("Design", "MA0016:Prefer using collection abstraction instead of implementation", Justification = "We need a list as we do need to marshal it as span.")]
+
+ public static TPolyline Encode(this IPolylineEncoder encoder, List coordinates)
- h4: Parameters
- parameters:
- name: encoder
@@ -54,43 +59,43 @@ body:
- text: IPolylineEncoder
url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html
- <
- - text: Coordinate
- url: PolylineAlgorithm.Coordinate.html
+ - TCoordinate
- ','
- " "
- - text: Polyline
- url: PolylineAlgorithm.Polyline.html
+ - TPolyline
- '>'
description: The instance used to perform the encoding operation.
- name: coordinates
type:
- - text: ICollection
- url: https://learn.microsoft.com/dotnet/api/system.collections.generic.icollection-1
+ - text: List
+ url: https://learn.microsoft.com/dotnet/api/system.collections.generic.list-1
- <
- - text: Coordinate
- url: PolylineAlgorithm.Coordinate.html
+ - TCoordinate
- '>'
description: The sequence of objects to encode.
- h4: Returns
- parameters:
- type:
- - text: Polyline
- url: PolylineAlgorithm.Polyline.html
+ - TPolyline
description: A representing the encoded polyline string for the provided coordinates.
+- h4: Type Parameters
+- parameters:
+ - name: TCoordinate
+ - name: TPolyline
- h4: Exceptions
- parameters:
- type:
- text: ArgumentNullException
url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception
description: Thrown when encoder is null .
-- api3: Encode(IPolylineEncoder, Coordinate[])
- id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode_PolylineAlgorithm_Abstraction_IPolylineEncoder_PolylineAlgorithm_Coordinate_PolylineAlgorithm_Polyline__PolylineAlgorithm_Coordinate___
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L55
+- api3: Encode(IPolylineEncoder, TCoordinate[])
+ id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L66
metadata:
- uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(PolylineAlgorithm.Abstraction.IPolylineEncoder{PolylineAlgorithm.Coordinate,PolylineAlgorithm.Polyline},PolylineAlgorithm.Coordinate[])
- commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(PolylineAlgorithm.Abstraction.IPolylineEncoder{PolylineAlgorithm.Coordinate,PolylineAlgorithm.Polyline},PolylineAlgorithm.Coordinate[])
+ uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[])
+ commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[])
- markdown: Encodes an array of instances into an encoded polyline.
-- code: public static Polyline Encode(this IPolylineEncoder encoder, Coordinate[] coordinates)
+- code: public static TPolyline Encode(this IPolylineEncoder encoder, TCoordinate[] coordinates)
- h4: Parameters
- parameters:
- name: encoder
@@ -98,27 +103,27 @@ body:
- text: IPolylineEncoder
url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html
- <
- - text: Coordinate
- url: PolylineAlgorithm.Coordinate.html
+ - TCoordinate
- ','
- " "
- - text: Polyline
- url: PolylineAlgorithm.Polyline.html
+ - TPolyline
- '>'
description: The instance used to perform the encoding operation.
- name: coordinates
type:
- - text: Coordinate
- url: PolylineAlgorithm.Coordinate.html
+ - TCoordinate
- '['
- ']'
description: The array of objects to encode.
- h4: Returns
- parameters:
- type:
- - text: Polyline
- url: PolylineAlgorithm.Polyline.html
+ - TPolyline
description: A representing the encoded polyline string for the provided coordinates.
+- h4: Type Parameters
+- parameters:
+ - name: TCoordinate
+ - name: TPolyline
- h4: Exceptions
- parameters:
- type:
diff --git a/api-reference/1.0/PolylineAlgorithm.InvalidPolylineException.yml b/api-reference/1.0/PolylineAlgorithm.InvalidPolylineException.yml
index fd2c31fe..31640d0a 100644
--- a/api-reference/1.0/PolylineAlgorithm.InvalidPolylineException.yml
+++ b/api-reference/1.0/PolylineAlgorithm.InvalidPolylineException.yml
@@ -3,7 +3,7 @@ title: Class InvalidPolylineException
body:
- api1: Class InvalidPolylineException
id: PolylineAlgorithm_InvalidPolylineException
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/InvalidPolylineException.cs#L19
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/InvalidPolylineException.cs#L23
metadata:
uid: PolylineAlgorithm.InvalidPolylineException
commentId: T:PolylineAlgorithm.InvalidPolylineException
@@ -16,6 +16,8 @@ body:
value: PolylineAlgorithm.dll
- markdown: Exception thrown when a polyline is determined to be malformed or invalid during processing.
- code: >-
+ [SuppressMessage("Roslynator", "RCS1194:Implement exception constructors", Justification = "Internal use only.")]
+
[SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "Internal use only.")]
public sealed class InvalidPolylineException : Exception, ISerializable
diff --git a/api-reference/1.0/PolylineAlgorithm.Polyline.yml b/api-reference/1.0/PolylineAlgorithm.Polyline.yml
index 5da10f97..c97a8826 100644
--- a/api-reference/1.0/PolylineAlgorithm.Polyline.yml
+++ b/api-reference/1.0/PolylineAlgorithm.Polyline.yml
@@ -3,7 +3,7 @@ title: Struct Polyline
body:
- api1: Struct Polyline
id: PolylineAlgorithm_Polyline
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L20
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L21
metadata:
uid: PolylineAlgorithm.Polyline
commentId: T:PolylineAlgorithm.Polyline
@@ -42,7 +42,7 @@ body:
- h2: Constructors
- api3: Polyline()
id: PolylineAlgorithm_Polyline__ctor
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L28
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L27
metadata:
uid: PolylineAlgorithm.Polyline.#ctor
commentId: M:PolylineAlgorithm.Polyline.#ctor
@@ -51,7 +51,7 @@ body:
- h2: Properties
- api3: IsEmpty
id: PolylineAlgorithm_Polyline_IsEmpty
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L50
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L49
metadata:
uid: PolylineAlgorithm.Polyline.IsEmpty
commentId: P:PolylineAlgorithm.Polyline.IsEmpty
@@ -64,21 +64,21 @@ body:
url: https://learn.microsoft.com/dotnet/api/system.boolean
- api3: Length
id: PolylineAlgorithm_Polyline_Length
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L55
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L54
metadata:
uid: PolylineAlgorithm.Polyline.Length
commentId: P:PolylineAlgorithm.Polyline.Length
-- markdown: Gets the length of the polyline in characters.
-- code: public long Length { get; }
+- markdown: Gets the length of the polyline in characters as an .
+- code: public int Length { get; }
- h4: Property Value
- parameters:
- type:
- - text: long
- url: https://learn.microsoft.com/dotnet/api/system.int64
+ - text: int
+ url: https://learn.microsoft.com/dotnet/api/system.int32
- h2: Methods
- api3: CopyTo(char[])
id: PolylineAlgorithm_Polyline_CopyTo_System_Char___
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L69
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L68
metadata:
uid: PolylineAlgorithm.Polyline.CopyTo(System.Char[])
commentId: M:PolylineAlgorithm.Polyline.CopyTo(System.Char[])
@@ -105,11 +105,11 @@ body:
description: Thrown when the length of destination does not match the polyline's length.
- api3: Equals(Polyline)
id: PolylineAlgorithm_Polyline_Equals_PolylineAlgorithm_Polyline_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L110
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L123
metadata:
uid: PolylineAlgorithm.Polyline.Equals(PolylineAlgorithm.Polyline)
commentId: M:PolylineAlgorithm.Polyline.Equals(PolylineAlgorithm.Polyline)
-- markdown: Indicates whether the current object is equal to another object of the same type.
+- markdown: Determines whether the current instance is equal to another instance.
- code: public bool Equals(Polyline other)
- h4: Parameters
- parameters:
@@ -117,27 +117,28 @@ body:
type:
- text: Polyline
url: PolylineAlgorithm.Polyline.html
- description: An object to compare with this object.
+ description: The other to compare with.
- h4: Returns
- parameters:
- type:
- text: bool
url: https://learn.microsoft.com/dotnet/api/system.boolean
- description: true if the current object is equal to the other parameter; otherwise, false .
-- api3: Equals(object)
+ description: true if the instances are equal; otherwise, false .
+- api3: Equals(object?)
id: PolylineAlgorithm_Polyline_Equals_System_Object_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L119
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L132
metadata:
uid: PolylineAlgorithm.Polyline.Equals(System.Object)
commentId: M:PolylineAlgorithm.Polyline.Equals(System.Object)
- markdown: Indicates whether this instance and a specified object are equal.
-- code: public override bool Equals(object obj)
+- code: public override bool Equals(object? obj)
- h4: Parameters
- parameters:
- name: obj
type:
- text: object
url: https://learn.microsoft.com/dotnet/api/system.object
+ - '?'
description: The object to compare with the current instance.
- h4: Returns
- parameters:
@@ -147,7 +148,7 @@ body:
description: true if obj and this instance are the same type and represent the same value; otherwise, false .
- api3: FromCharArray(char[])
id: PolylineAlgorithm_Polyline_FromCharArray_System_Char___
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L168
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L188
metadata:
uid: PolylineAlgorithm.Polyline.FromCharArray(System.Char[])
commentId: M:PolylineAlgorithm.Polyline.FromCharArray(System.Char[])
@@ -176,7 +177,7 @@ body:
description: Thrown when polyline is null .
- api3: FromMemory(ReadOnlyMemory)
id: PolylineAlgorithm_Polyline_FromMemory_System_ReadOnlyMemory_System_Char__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L205
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L225
metadata:
uid: PolylineAlgorithm.Polyline.FromMemory(System.ReadOnlyMemory{System.Char})
commentId: M:PolylineAlgorithm.Polyline.FromMemory(System.ReadOnlyMemory{System.Char})
@@ -201,7 +202,7 @@ body:
description: The instance corresponding to the specified memory region.
- api3: FromString(string)
id: PolylineAlgorithm_Polyline_FromString_System_String_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L188
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L208
metadata:
uid: PolylineAlgorithm.Polyline.FromString(System.String)
commentId: M:PolylineAlgorithm.Polyline.FromString(System.String)
@@ -228,7 +229,7 @@ body:
description: Thrown when polyline is null .
- api3: GetHashCode()
id: PolylineAlgorithm_Polyline_GetHashCode
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L124
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L137
metadata:
uid: PolylineAlgorithm.Polyline.GetHashCode
commentId: M:PolylineAlgorithm.Polyline.GetHashCode
@@ -242,7 +243,7 @@ body:
description: A 32-bit signed integer that is the hash code for this instance.
- api3: ToString()
id: PolylineAlgorithm_Polyline_ToString
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L87
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L86
metadata:
uid: PolylineAlgorithm.Polyline.ToString
commentId: M:PolylineAlgorithm.Polyline.ToString
@@ -257,7 +258,7 @@ body:
- h2: Operators
- api3: operator ==(Polyline, Polyline)
id: PolylineAlgorithm_Polyline_op_Equality_PolylineAlgorithm_Polyline_PolylineAlgorithm_Polyline_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L140
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L162
metadata:
uid: PolylineAlgorithm.Polyline.op_Equality(PolylineAlgorithm.Polyline,PolylineAlgorithm.Polyline)
commentId: M:PolylineAlgorithm.Polyline.op_Equality(PolylineAlgorithm.Polyline,PolylineAlgorithm.Polyline)
@@ -283,7 +284,7 @@ body:
description: true if the polylines are equal; otherwise, false .
- api3: operator !=(Polyline, Polyline)
id: PolylineAlgorithm_Polyline_op_Inequality_PolylineAlgorithm_Polyline_PolylineAlgorithm_Polyline_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/Polyline.cs#L150
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Polyline.cs#L172
metadata:
uid: PolylineAlgorithm.Polyline.op_Inequality(PolylineAlgorithm.Polyline,PolylineAlgorithm.Polyline)
commentId: M:PolylineAlgorithm.Polyline.op_Inequality(PolylineAlgorithm.Polyline,PolylineAlgorithm.Polyline)
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineDecoder.yml b/api-reference/1.0/PolylineAlgorithm.PolylineDecoder.yml
index 446fdd9c..98a08412 100644
--- a/api-reference/1.0/PolylineAlgorithm.PolylineDecoder.yml
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineDecoder.yml
@@ -3,7 +3,7 @@ title: Class PolylineDecoder
body:
- api1: Class PolylineDecoder
id: PolylineAlgorithm_PolylineDecoder
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L11
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L11
metadata:
uid: PolylineAlgorithm.PolylineDecoder
commentId: T:PolylineAlgorithm.PolylineDecoder
@@ -37,6 +37,8 @@ body:
url: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.html#PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Options
- text: AbstractPolylineDecoder.Decode(Polyline)
url: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.html#PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Decode__0_
+ - text: AbstractPolylineDecoder.Decode(Polyline, CancellationToken)
+ url: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.html#PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Decode__0_System_Threading_CancellationToken_
- text: object.Equals(object)
url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
- text: object.Equals(object, object)
@@ -62,7 +64,7 @@ body:
- h2: Constructors
- api3: PolylineDecoder()
id: PolylineAlgorithm_PolylineDecoder__ctor
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L13
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L13
metadata:
uid: PolylineAlgorithm.PolylineDecoder.#ctor
commentId: M:PolylineAlgorithm.PolylineDecoder.#ctor
@@ -70,7 +72,7 @@ body:
- code: public PolylineDecoder()
- api3: PolylineDecoder(PolylineEncodingOptions)
id: PolylineAlgorithm_PolylineDecoder__ctor_PolylineAlgorithm_PolylineEncodingOptions_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L17
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L17
metadata:
uid: PolylineAlgorithm.PolylineDecoder.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
commentId: M:PolylineAlgorithm.PolylineDecoder.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
@@ -92,11 +94,10 @@ body:
- h2: Methods
- api3: CreateCoordinate(double, double)
id: PolylineAlgorithm_PolylineDecoder_CreateCoordinate_System_Double_System_Double_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L21
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L21
metadata:
uid: PolylineAlgorithm.PolylineDecoder.CreateCoordinate(System.Double,System.Double)
commentId: M:PolylineAlgorithm.PolylineDecoder.CreateCoordinate(System.Double,System.Double)
-- markdown: Creates a coordinate instance from the given latitude and longitude values.
- code: protected override Coordinate CreateCoordinate(double latitude, double longitude)
- h4: Parameters
- parameters:
@@ -104,33 +105,28 @@ body:
type:
- text: double
url: https://learn.microsoft.com/dotnet/api/system.double
- description: The latitude value.
- name: longitude
type:
- text: double
url: https://learn.microsoft.com/dotnet/api/system.double
- description: The longitude value.
- h4: Returns
- parameters:
- type:
- text: Coordinate
url: PolylineAlgorithm.Coordinate.html
- description: A coordinate instance of type .
-- api3: GetReadOnlyMemory(Polyline)
- id: PolylineAlgorithm_PolylineDecoder_GetReadOnlyMemory_PolylineAlgorithm_Polyline_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L26
+- api3: GetReadOnlyMemory(in Polyline)
+ id: PolylineAlgorithm_PolylineDecoder_GetReadOnlyMemory_PolylineAlgorithm_Polyline__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineDecoder.cs#L26
metadata:
- uid: PolylineAlgorithm.PolylineDecoder.GetReadOnlyMemory(PolylineAlgorithm.Polyline)
- commentId: M:PolylineAlgorithm.PolylineDecoder.GetReadOnlyMemory(PolylineAlgorithm.Polyline)
-- markdown: Converts the provided polyline instance into a for decoding.
-- code: protected override ReadOnlyMemory GetReadOnlyMemory(Polyline polyline)
+ uid: PolylineAlgorithm.PolylineDecoder.GetReadOnlyMemory(PolylineAlgorithm.Polyline@)
+ commentId: M:PolylineAlgorithm.PolylineDecoder.GetReadOnlyMemory(PolylineAlgorithm.Polyline@)
+- code: protected override ReadOnlyMemory GetReadOnlyMemory(in Polyline polyline)
- h4: Parameters
- parameters:
- name: polyline
type:
- text: Polyline
url: PolylineAlgorithm.Polyline.html
- description: The instance containing the encoded polyline data to decode.
- h4: Returns
- parameters:
- type:
@@ -140,7 +136,6 @@ body:
- text: char
url: https://learn.microsoft.com/dotnet/api/system.char
- '>'
- description: A representing the encoded polyline data.
languageId: csharp
metadata:
description: >-
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineEncoder.yml b/api-reference/1.0/PolylineAlgorithm.PolylineEncoder.yml
index 2cc3a5dd..25c5add2 100644
--- a/api-reference/1.0/PolylineAlgorithm.PolylineEncoder.yml
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineEncoder.yml
@@ -3,7 +3,7 @@ title: Class PolylineEncoder
body:
- api1: Class PolylineEncoder
id: PolylineAlgorithm_PolylineEncoder
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L11
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L11
metadata:
uid: PolylineAlgorithm.PolylineEncoder
commentId: T:PolylineAlgorithm.PolylineEncoder
@@ -35,8 +35,8 @@ body:
- list:
- text: AbstractPolylineEncoder.Options
url: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.html#PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Options
- - text: AbstractPolylineEncoder.Encode(IEnumerable)
- url: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.html#PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Encode_System_Collections_Generic_IEnumerable__0__
+ - text: AbstractPolylineEncoder.Encode(ReadOnlySpan)
+ url: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.html#PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Encode_System_ReadOnlySpan__0__
- text: object.Equals(object)
url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
- text: object.Equals(object, object)
@@ -51,16 +51,16 @@ body:
url: https://learn.microsoft.com/dotnet/api/system.object.tostring
- h4: Extension Methods
- list:
- - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, ICollection)
- url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode_PolylineAlgorithm_Abstraction_IPolylineEncoder_PolylineAlgorithm_Coordinate_PolylineAlgorithm_Polyline__System_Collections_Generic_ICollection_PolylineAlgorithm_Coordinate__
- - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, Coordinate[])
- url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode_PolylineAlgorithm_Abstraction_IPolylineEncoder_PolylineAlgorithm_Coordinate_PolylineAlgorithm_Polyline__PolylineAlgorithm_Coordinate___
+ - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, List)
+ url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__
+ - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, Coordinate[])
+ url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___
- h2: Remarks
- markdown: This abstract class serves as a base for specific polyline encoders, allowing customization of the encoding process.
- h2: Constructors
- api3: PolylineEncoder()
id: PolylineAlgorithm_PolylineEncoder__ctor
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L13
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L13
metadata:
uid: PolylineAlgorithm.PolylineEncoder.#ctor
commentId: M:PolylineAlgorithm.PolylineEncoder.#ctor
@@ -68,7 +68,7 @@ body:
- code: public PolylineEncoder()
- api3: PolylineEncoder(PolylineEncodingOptions)
id: PolylineAlgorithm_PolylineEncoder__ctor_PolylineAlgorithm_PolylineEncodingOptions_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L17
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L17
metadata:
uid: PolylineAlgorithm.PolylineEncoder.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
commentId: M:PolylineAlgorithm.PolylineEncoder.#ctor(PolylineAlgorithm.PolylineEncodingOptions)
@@ -90,7 +90,7 @@ body:
- h2: Methods
- api3: CreatePolyline(ReadOnlyMemory)
id: PolylineAlgorithm_PolylineEncoder_CreatePolyline_System_ReadOnlyMemory_System_Char__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L31
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L31
metadata:
uid: PolylineAlgorithm.PolylineEncoder.CreatePolyline(System.ReadOnlyMemory{System.Char})
commentId: M:PolylineAlgorithm.PolylineEncoder.CreatePolyline(System.ReadOnlyMemory{System.Char})
@@ -115,7 +115,7 @@ body:
description: An instance of representing the encoded polyline.
- api3: GetLatitude(Coordinate)
id: PolylineAlgorithm_PolylineEncoder_GetLatitude_PolylineAlgorithm_Coordinate_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L21
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L21
metadata:
uid: PolylineAlgorithm.PolylineEncoder.GetLatitude(PolylineAlgorithm.Coordinate)
commentId: M:PolylineAlgorithm.PolylineEncoder.GetLatitude(PolylineAlgorithm.Coordinate)
@@ -135,7 +135,7 @@ body:
description: The latitude value as a .
- api3: GetLongitude(Coordinate)
id: PolylineAlgorithm_PolylineEncoder_GetLongitude_PolylineAlgorithm_Coordinate_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L26
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoder.cs#L26
metadata:
uid: PolylineAlgorithm.PolylineEncoder.GetLongitude(PolylineAlgorithm.Coordinate)
commentId: M:PolylineAlgorithm.PolylineEncoder.GetLongitude(PolylineAlgorithm.Coordinate)
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.Validation.yml b/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.Validation.yml
new file mode 100644
index 00000000..738205ac
--- /dev/null
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.Validation.yml
@@ -0,0 +1,157 @@
+### YamlMime:ApiPage
+title: Class PolylineEncoding.Validation
+body:
+- api1: Class PolylineEncoding.Validation
+ id: PolylineAlgorithm_PolylineEncoding_Validation
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L322
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validation
+ commentId: T:PolylineAlgorithm.PolylineEncoding.Validation
+- facts:
+ - name: Namespace
+ value:
+ text: PolylineAlgorithm
+ url: PolylineAlgorithm.html
+ - name: Assembly
+ value: PolylineAlgorithm.dll
+- code: public static class PolylineEncoding.Validation
+- h4: Inheritance
+- inheritance:
+ - text: object
+ url: https://learn.microsoft.com/dotnet/api/system.object
+ - text: PolylineEncoding.Validation
+ url: PolylineAlgorithm.PolylineEncoding.Validation.html
+- h4: Inherited Members
+- list:
+ - text: object.Equals(object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
+ - text: object.Equals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)
+ - text: object.GetHashCode()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode
+ - text: object.GetType()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gettype
+ - text: object.MemberwiseClone()
+ url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone
+ - text: object.ReferenceEquals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
+ - text: object.ToString()
+ url: https://learn.microsoft.com/dotnet/api/system.object.tostring
+- h2: Methods
+- api3: ValidateBlockLength(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_Validation_ValidateBlockLength_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L419
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validation.ValidateBlockLength(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Validation.ValidateBlockLength(System.ReadOnlySpan{System.Char})
+- markdown: Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly.
+- code: public static void ValidateBlockLength(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value).
+
+ Throws an if any block exceeds 7 characters or if the polyline does not end with a valid block terminator.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator.
+- api3: ValidateCharRange(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_Validation_ValidateCharRange_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L373
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validation.ValidateCharRange(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Validation.ValidateCharRange(System.ReadOnlySpan{System.Char})
+- markdown: Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding.
+- code: public static void ValidateCharRange(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected.
+
+
+
+
+
+ The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an is thrown.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when an invalid character is found in the polyline segment.
+- api3: ValidateFormat(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_Validation_ValidateFormat_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L351
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validation.ValidateFormat(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Validation.ValidateFormat(System.ReadOnlySpan{System.Char})
+- markdown: Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct.
+- code: public static void ValidateFormat(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ This method performs two levels of validation on the provided polyline segment:
+
+
+
+
+ Character Range Validation: Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive).
+ Uses SIMD acceleration for efficient validation of large segments.
+
+ Block Structure Validation: Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator.
+
+
+
+ If an invalid character or block structure is detected, an is thrown with details about the error.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when an invalid character is found or the block structure is invalid.
+languageId: csharp
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.Validator.yml b/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.Validator.yml
new file mode 100644
index 00000000..8f58348f
--- /dev/null
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.Validator.yml
@@ -0,0 +1,157 @@
+### YamlMime:ApiPage
+title: Class PolylineEncoding.Validator
+body:
+- api1: Class PolylineEncoding.Validator
+ id: PolylineAlgorithm_PolylineEncoding_Validator
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L321
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validator
+ commentId: T:PolylineAlgorithm.PolylineEncoding.Validator
+- facts:
+ - name: Namespace
+ value:
+ text: PolylineAlgorithm
+ url: PolylineAlgorithm.html
+ - name: Assembly
+ value: PolylineAlgorithm.dll
+- code: public static class PolylineEncoding.Validator
+- h4: Inheritance
+- inheritance:
+ - text: object
+ url: https://learn.microsoft.com/dotnet/api/system.object
+ - text: PolylineEncoding.Validator
+ url: PolylineAlgorithm.PolylineEncoding.Validator.html
+- h4: Inherited Members
+- list:
+ - text: object.Equals(object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
+ - text: object.Equals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)
+ - text: object.GetHashCode()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode
+ - text: object.GetType()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gettype
+ - text: object.MemberwiseClone()
+ url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone
+ - text: object.ReferenceEquals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
+ - text: object.ToString()
+ url: https://learn.microsoft.com/dotnet/api/system.object.tostring
+- h2: Methods
+- api3: ValidateBlockLength(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_Validator_ValidateBlockLength_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L418
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validator.ValidateBlockLength(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Validator.ValidateBlockLength(System.ReadOnlySpan{System.Char})
+- markdown: Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly.
+- code: public static void ValidateBlockLength(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value).
+
+ Throws an if any block exceeds 7 characters or if the polyline does not end with a valid block terminator.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator.
+- api3: ValidateCharRange(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_Validator_ValidateCharRange_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L372
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validator.ValidateCharRange(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Validator.ValidateCharRange(System.ReadOnlySpan{System.Char})
+- markdown: Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding.
+- code: public static void ValidateCharRange(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected.
+
+
+
+
+
+ The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an is thrown.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when an invalid character is found in the polyline segment.
+- api3: ValidateFormat(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_Validator_ValidateFormat_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L350
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.Validator.ValidateFormat(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Validator.ValidateFormat(System.ReadOnlySpan{System.Char})
+- markdown: Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct.
+- code: public static void ValidateFormat(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ This method performs two levels of validation on the provided polyline segment:
+
+
+
+
+ Character Range Validation: Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive).
+ Uses SIMD acceleration for efficient validation of large segments.
+
+ Block Structure Validation: Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator.
+
+
+
+ If an invalid character or block structure is detected, an is thrown with details about the error.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when an invalid character is found or the block structure is invalid.
+languageId: csharp
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.yml b/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.yml
index 8b249376..5b61ad77 100644
--- a/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.yml
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineEncoding.yml
@@ -3,7 +3,7 @@ title: Class PolylineEncoding
body:
- api1: Class PolylineEncoding
id: PolylineAlgorithm_PolylineEncoding
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L19
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L23
metadata:
uid: PolylineAlgorithm.PolylineEncoding
commentId: T:PolylineAlgorithm.PolylineEncoding
@@ -48,136 +48,239 @@ body:
coordinates. It also provides validation utilities to ensure values conform to expected ranges for latitude and
longitude.
- h2: Methods
-- api3: Denormalize(int, CoordinateValueType)
- id: PolylineAlgorithm_PolylineEncoding_Denormalize_System_Int32_PolylineAlgorithm_CoordinateValueType_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L91
+- api3: Denormalize(int, uint)
+ id: PolylineAlgorithm_PolylineEncoding_Denormalize_System_Int32_System_UInt32_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L121
metadata:
- uid: PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,PolylineAlgorithm.CoordinateValueType)
- commentId: M:PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,PolylineAlgorithm.CoordinateValueType)
-- markdown: Converts a normalized integer value to its denormalized double representation based on the specified type.
-- code: public static double Denormalize(int value, CoordinateValueType type)
+ uid: PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,System.UInt32)
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,System.UInt32)
+- markdown: Converts a normalized integer coordinate value back to its floating-point representation based on the specified precision.
+- code: public static double Denormalize(int value, uint precision = 5)
- h4: Parameters
- parameters:
- name: value
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: The normalized integer value to be denormalized. Must be within the valid range for the specified type.
- - name: type
+ description: The integer value to denormalize. Typically produced by the method.
+ - name: precision
type:
- - text: CoordinateValueType
- url: PolylineAlgorithm.CoordinateValueType.html
- description: The type that defines the valid range for value.
+ - text: uint
+ url: https://learn.microsoft.com/dotnet/api/system.uint32
+ description: The number of decimal places used during normalization. Default is 5, matching standard polyline encoding precision.
+ optional: true
- h4: Returns
- parameters:
- type:
- text: double
url: https://learn.microsoft.com/dotnet/api/system.double
- description: The denormalized double representation of the input value. Returns 0.0 if value is 0.
+ description: The denormalized floating-point coordinate value.
- h4: Remarks
- markdown: >-
- The denormalization process divides the input value by a predefined precision factor to
- produce the resulting double. Ensure that value is validated against the specified type before calling this method.
+
+
+ This method reverses the normalization performed by . It takes an integer value and converts it
+
+ to a double by dividing it by 10 raised to the power of the specified precision. If precision is 0,
+
+ the value is returned as a double without division.
+
+
+
+
+
+ The calculation is performed inside a checked block to ensure that any arithmetic overflow is detected
+
+ and an is thrown.
+
+
+
+
+
+ For example, with a precision of 5:
+
+
+
A value of 3778903 becomes 37.78903 A value of -12241230 becomes -122.4123
+
+
+
+
+
+ If the input value is 0, the method returns 0.0 immediately.
+
+
- h4: Exceptions
- parameters:
- type:
- - text: ArgumentOutOfRangeException
- url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception
- description: Thrown when value is outside the valid range for the specified type.
-- api3: GetCharCount(int)
- id: PolylineAlgorithm_PolylineEncoding_GetCharCount_System_Int32_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L222
+ - text: OverflowException
+ url: https://learn.microsoft.com/dotnet/api/system.overflowexception
+ description: Thrown if the arithmetic operation overflows during conversion.
+- api3: GetRequiredBufferSize(int)
+ id: PolylineAlgorithm_PolylineEncoding_GetRequiredBufferSize_System_Int32_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L297
metadata:
- uid: PolylineAlgorithm.PolylineEncoding.GetCharCount(System.Int32)
- commentId: M:PolylineAlgorithm.PolylineEncoding.GetCharCount(System.Int32)
-- markdown: >-
- Determines the number of characters required to represent the specified integer value within predefined
-
- variance ranges.
-- code: public static int GetCharCount(int variance)
+ uid: PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int32)
+ commentId: M:PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int32)
+- markdown: Calculates the number of characters required to encode a delta value in polyline format.
+- code: public static int GetRequiredBufferSize(int delta)
- h4: Parameters
- parameters:
- - name: variance
+ - name: delta
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
description: >-
- The integer value for which the character count is calculated. Must be within the range of a 32-bit signed
+ The integer delta value to calculate the encoded size for. This value typically represents the difference between
- integer.
+ consecutive coordinate values in polyline encoding.
- h4: Returns
- parameters:
- type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: >-
- The number of characters required to represent the variance value, based on its magnitude.
-
- Returns a value between 1 and 6 inclusive.
+ description: The number of characters required to encode the specified delta value. The minimum return value is 1.
- h4: Remarks
- markdown: >-
- The method uses predefined ranges to efficiently determine the character count. Smaller
+
+
+ This method determines how many characters will be needed to represent an integer delta value when encoded
+
+ using the polyline encoding algorithm. It performs the same zigzag encoding transformation as
+
+ but only calculates the required buffer size without actually writing any data.
+
+
+
+
+
+ The calculation process:
- values require fewer characters, while larger values require more. This method is optimized for performance
- using a switch expression.
-- api3: Normalize(double, CoordinateValueType)
- id: PolylineAlgorithm_PolylineEncoding_Normalize_System_Double_PolylineAlgorithm_CoordinateValueType_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L180
+
Applies zigzag encoding: left-shifts the value by 1 bit, then inverts all bits if the original value was negative Counts how many 5-bit chunks are needed to represent the encoded value Each chunk requires one character, with a minimum of 1 character for any value
+
+
+
+
+
+ This method is useful for pre-allocating buffers of the correct size before encoding polyline data, helping to avoid
+
+ buffer overflow checks during the actual encoding process.
+
+
+
+
+
+ The method uses a long internally to prevent overflow during the left-shift operation on large negative values.
+
+
+- h4: See Also
+- list:
+ - - text: PolylineEncoding
+ url: PolylineAlgorithm.PolylineEncoding.html
+ - .
+ - text: TryWriteValue
+ url: PolylineAlgorithm.PolylineEncoding.html#PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int32_System_Span_System_Char__System_Int32__
+ - (
+ - text: int
+ url: https://learn.microsoft.com/dotnet/api/system.int32
+ - ','
+ - " "
+ - text: Span
+ url: https://learn.microsoft.com/dotnet/api/system.span-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ - ','
+ - " "
+ - ref
+ - " "
+ - text: int
+ url: https://learn.microsoft.com/dotnet/api/system.int32
+ - )
+- api3: Normalize(double, uint)
+ id: PolylineAlgorithm_PolylineEncoding_Normalize_System_Double_System_UInt32_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L61
metadata:
- uid: PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,PolylineAlgorithm.CoordinateValueType)
- commentId: M:PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,PolylineAlgorithm.CoordinateValueType)
-- markdown: Normalizes a given numeric value based on the specified type and precision settings.
-- code: public static int Normalize(double value, CoordinateValueType type)
+ uid: PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32)
+ commentId: M:PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32)
+- markdown: Normalizes a geographic coordinate value to an integer representation based on the specified precision.
+- code: public static int Normalize(double value, uint precision = 5)
- h4: Parameters
- parameters:
- name: value
type:
- text: double
url: https://learn.microsoft.com/dotnet/api/system.double
- description: The numeric value to normalize. Must be a finite number.
- - name: type
+ description: The numeric value to normalize. Must be a finite number (not NaN or infinity).
+ - name: precision
type:
- - text: CoordinateValueType
- url: PolylineAlgorithm.CoordinateValueType.html
- description: The type against which the value is validated. Determines the acceptable range for the value.
+ - text: uint
+ url: https://learn.microsoft.com/dotnet/api/system.uint32
+ description: >-
+ The number of decimal places of precision to preserve in the normalized value.
+
+ The value is multiplied by 10^precision before rounding.
+
+ Default is 5, which is standard for polyline encoding.
+ optional: true
- h4: Returns
- parameters:
- type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: An integer representing the normalized value. Returns 0 if the input value is 0.0.
+ description: An integer representing the normalized value. Returns 0 if the input value is 0.0.
- h4: Remarks
- markdown: >-
- This method validates the input value to ensure it is finite and within the acceptable range
+
+
+ This method converts a floating-point coordinate value into a normalized integer by multiplying it by 10 raised
+
+ to the power of the specified precision, then rounding the result using the specified rounding strategy.
+
+
+
+
+
+ For example, with the default precision of 5:
- for the specified type. If the value is valid, it applies a normalization algorithm using a predefined precision
- factor.
+
A value of 37.78903 becomes 3778903 A value of -122.4123 becomes -12241230
+
+
+
+
+
+ The method validates that the input value is finite (not NaN or infinity) before performing normalization.
+
+ If the precision is 0, the value is rounded without multiplication.
+
+
- h4: Exceptions
- parameters:
- type:
- text: ArgumentOutOfRangeException
url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception
- description: >-
- Thrown when value is not a finite number or is outside the valid range for the specified
-
- type.
-- api3: TryReadValue(ref int, ref ReadOnlyMemory, ref int)
- id: PolylineAlgorithm_PolylineEncoding_TryReadValue_System_Int32__System_ReadOnlyMemory_System_Char___System_Int32__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L40
+ description: Thrown when value is not a finite number (NaN or infinity).
+ - type:
+ - text: OverflowException
+ url: https://learn.microsoft.com/dotnet/api/system.overflowexception
+ description: Thrown when the normalized result exceeds the range of a 32-bit signed integer during the conversion from double to int.
+- api3: TryReadValue(ref int, ReadOnlyMemory, ref int)
+ id: PolylineAlgorithm_PolylineEncoding_TryReadValue_System_Int32__System_ReadOnlyMemory_System_Char__System_Int32__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L168
metadata:
- uid: PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char}@,System.Int32@)
- commentId: M:PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char}@,System.Int32@)
-- markdown: Attempts to read a value from the specified buffer and updates the variance.
-- code: public static bool TryReadValue(ref int variance, ref ReadOnlyMemory buffer, ref int position)
+ uid: PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char},System.Int32@)
+ commentId: M:PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char},System.Int32@)
+- markdown: Attempts to read an encoded integer value from a polyline buffer, updating the specified delta and position.
+- code: public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref int position)
- h4: Parameters
- parameters:
- - name: variance
+ - name: delta
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: A reference to the integer that will be updated based on the value read from the buffer.
+ description: Reference to the integer accumulator that will be updated with the decoded value.
- name: buffer
type:
- text: ReadOnlyMemory
@@ -186,38 +289,61 @@ body:
- text: char
url: https://learn.microsoft.com/dotnet/api/system.char
- '>'
- description: A reference to the read-only memory buffer containing the data to be processed.
+ description: The buffer containing polyline-encoded characters.
- name: position
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: A reference to the current position within the buffer. The position is incremented as the method reads data.
+ description: Reference to the current position in the buffer. This value is updated as characters are read.
- h4: Returns
- parameters:
- type:
- text: bool
url: https://learn.microsoft.com/dotnet/api/system.boolean
- description: true if a value was successfully read and the end of the buffer was not reached; otherwise, false .
+ description: true if a value was successfully read and decoded; false if the buffer ended before a complete value was read.
- h4: Remarks
- markdown: >-
- This method processes the buffer starting at the specified position and attempts to decode a value.
- The decoded value is used to update the variance parameter. The method stops reading when a
- termination condition is met or the end of the buffer is reached.
-- api3: TryWriteValue(int, ref Span, ref int)
- id: PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int32_System_Span_System_Char___System_Int32__
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L133
+
+
+ This method decodes a value from a polyline-encoded character buffer, starting at the given position. It reads
+
+ characters sequentially, applying the polyline decoding algorithm, and updates the delta with
+
+ the decoded value. The position is advanced as characters are processed.
+
+
+
+
+
+ The decoding process continues until a character with a value less than the algorithm's space constant is encountered,
+
+ which signals the end of the encoded value. If the buffer is exhausted before a complete value is read, the method returns false .
+
+
+
+
+
+ The decoded value is added to delta using zigzag decoding, which handles both positive and negative values.
+
+
+- api3: TryWriteValue(int, Span, ref int)
+ id: PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int32_System_Span_System_Char__System_Int32__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L236
metadata:
- uid: PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char}@,System.Int32@)
- commentId: M:PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char}@,System.Int32@)
-- markdown: Attempts to write a value derived from the specified variance into the provided buffer at the given position.
-- code: public static bool TryWriteValue(int variance, ref Span buffer, ref int position)
+ uid: PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char},System.Int32@)
+ commentId: M:PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char},System.Int32@)
+- markdown: Attempts to write an encoded integer value to a polyline buffer, updating the specified position.
+- code: public static bool TryWriteValue(int delta, Span buffer, ref int position)
- h4: Parameters
- parameters:
- - name: variance
+ - name: delta
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: The integer value used to calculate the output to be written into the buffer.
+ description: >-
+ The integer value to encode and write to the buffer. This value typically represents the difference between consecutive
+
+ coordinate values in polyline encoding.
- name: buffer
type:
- text: Span
@@ -226,26 +352,175 @@ body:
- text: char
url: https://learn.microsoft.com/dotnet/api/system.char
- '>'
- description: A reference to the span of characters where the value will be written.
+ description: The destination buffer where the encoded characters will be written. Must have sufficient capacity to hold the encoded value.
- name: position
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
description: >-
- A reference to the current position in the buffer where writing begins. This value is updated to reflect the new
+ Reference to the current position in the buffer. This value is updated as characters are written to reflect the new position
- position after writing.
+ after encoding is complete.
- h4: Returns
- parameters:
- type:
- text: bool
url: https://learn.microsoft.com/dotnet/api/system.boolean
- description: true if the value was successfully written to the buffer; otherwise, false .
+ description: >-
+ true if the value was successfully encoded and written to the buffer; false if the buffer
+
+ does not have sufficient remaining capacity to hold the encoded value.
+- h4: Remarks
+- markdown: >-
+
+
+ This method encodes an integer delta value into a polyline-encoded format and writes it to the provided character buffer,
+
+ starting at the given position. It applies zigzag encoding followed by the polyline encoding algorithm to represent
+
+ both positive and negative values efficiently.
+
+
+
+
+
+ The encoding process first converts the value using zigzag encoding (left shift by 1, with bitwise inversion for negative values),
+
+ then writes it as a sequence of characters. Each character encodes 5 bits of data, with continuation bits indicating whether
+
+ more characters follow. The position is advanced as characters are written.
+
+
+
+
+
+ Before writing, the method validates that sufficient space is available in the buffer by calling GetCharCount.
+
+ If the buffer does not have enough remaining capacity, the method returns false without modifying the buffer or position.
+
+
+
+
+
+ This method is the inverse of and can be used to encode coordinate deltas for polyline serialization.
+
+
+- api3: ValidateBlockLength(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_ValidateBlockLength_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L437
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char})
+- markdown: Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly.
+- code: public static void ValidateBlockLength(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value).
+
+ Throws an if any block exceeds 7 characters or if the polyline does not end with a valid block terminator.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator.
+- api3: ValidateCharRange(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_ValidateCharRange_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L391
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char})
+- markdown: Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding.
+- code: public static void ValidateCharRange(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
+- h4: Remarks
+- markdown: >-
+
+
+ Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected.
+
+
+
+
+
+ The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an is thrown.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when an invalid character is found in the polyline segment.
+- api3: ValidateFormat(ReadOnlySpan)
+ id: PolylineAlgorithm_PolylineEncoding_ValidateFormat_System_ReadOnlySpan_System_Char__
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncoding.cs#L369
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char})
+ commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char})
+- markdown: Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct.
+- code: public static void ValidateFormat(ReadOnlySpan polyline)
+- h4: Parameters
+- parameters:
+ - name: polyline
+ type:
+ - text: ReadOnlySpan
+ url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1
+ - <
+ - text: char
+ url: https://learn.microsoft.com/dotnet/api/system.char
+ - '>'
+ description: A span representing the polyline segment to validate.
- h4: Remarks
- markdown: >-
- This method performs bounds checking to ensure that the buffer has sufficient space to
+
+
+ This method performs two levels of validation on the provided polyline segment:
+
+
+
+
+ Character Range Validation: Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive).
+ Uses SIMD acceleration for efficient validation of large segments.
+
+ Block Structure Validation: Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator.
+
+
- accommodate the calculated value. If the buffer does not have enough space, the method returns false without modifying the buffer or position.
+ If an invalid character or block structure is detected, an is thrown with details about the error.
+
+
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: ArgumentException
+ url: https://learn.microsoft.com/dotnet/api/system.argumentexception
+ description: Thrown when an invalid character is found or the block structure is invalid.
languageId: csharp
metadata:
description: >-
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptions.yml b/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptions.yml
index 49c71468..2e3880ce 100644
--- a/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptions.yml
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptions.yml
@@ -3,7 +3,7 @@ title: Class PolylineEncodingOptions
body:
- api1: Class PolylineEncodingOptions
id: PolylineAlgorithm_PolylineEncodingOptions
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L18
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L29
metadata:
uid: PolylineAlgorithm.PolylineEncodingOptions
commentId: T:PolylineAlgorithm.PolylineEncodingOptions
@@ -14,7 +14,7 @@ body:
url: PolylineAlgorithm.html
- name: Assembly
value: PolylineAlgorithm.dll
-- markdown: Options for configuring polyline encoding.
+- markdown: Provides configuration options for polyline encoding operations.
- code: public sealed class PolylineEncodingOptions
- h4: Inheritance
- inheritance:
@@ -37,15 +37,28 @@ body:
- text: object.ToString()
url: https://learn.microsoft.com/dotnet/api/system.object.tostring
- h2: Remarks
-- markdown: This class allows you to set options such as buffer size and logger factory for encoding operations.
+- markdown: >-
+
+
+ This class allows you to configure various aspects of polyline encoding, including:
+
+
+
+ The level for coordinate encoding The for memory allocation strategy The for diagnostic logging
+
+
+
+ All properties have internal setters and should be configured through a builder or factory pattern.
+
+
- h2: Properties
- api3: LoggerFactory
id: PolylineAlgorithm_PolylineEncodingOptions_LoggerFactory
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L34
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L41
metadata:
uid: PolylineAlgorithm.PolylineEncodingOptions.LoggerFactory
commentId: P:PolylineAlgorithm.PolylineEncodingOptions.LoggerFactory
-- markdown: Gets or sets the precision for encoding coordinates.
+- markdown: Gets the logger factory used for diagnostic logging during encoding operations.
- code: public ILoggerFactory LoggerFactory { get; }
- h4: Property Value
- parameters:
@@ -53,22 +66,62 @@ body:
- text: ILoggerFactory
url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerfactory
- h4: Remarks
-- markdown: The default logger factory is , which does not log any messages.
-- api3: MaxBufferSize
- id: PolylineAlgorithm_PolylineEncodingOptions_MaxBufferSize
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L26
+- markdown: >-
+ The default logger factory is , which does not log any messages.
+
+ To enable logging, provide a custom implementation.
+- api3: Precision
+ id: PolylineAlgorithm_PolylineEncodingOptions_Precision
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L60
metadata:
- uid: PolylineAlgorithm.PolylineEncodingOptions.MaxBufferSize
- commentId: P:PolylineAlgorithm.PolylineEncodingOptions.MaxBufferSize
-- markdown: Gets the maximum buffer size for encoding operations.
-- code: public int MaxBufferSize { get; }
+ uid: PolylineAlgorithm.PolylineEncodingOptions.Precision
+ commentId: P:PolylineAlgorithm.PolylineEncodingOptions.Precision
+- markdown: Gets the precision level used for encoding coordinate values.
+- code: public uint Precision { get; }
+- h4: Property Value
+- parameters:
+ - type:
+ - text: uint
+ url: https://learn.microsoft.com/dotnet/api/system.uint32
+- h4: Remarks
+- markdown: >-
+
+
+ The precision determines the number of decimal places to which each coordinate value (latitude or longitude)
+
+ is multiplied and truncated (not rounded) before encoding. For example, a precision of 5 means each coordinate is multiplied by 10^5
+
+ and truncated to an integer before encoding.
+
+
+
+
+
+ This setting does not directly correspond to a physical distance or accuracy in meters, but rather controls
+
+ the granularity of the encoded values.
+
+
+- api3: StackAllocLimit
+ id: PolylineAlgorithm_PolylineEncodingOptions_StackAllocLimit
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L73
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncodingOptions.StackAllocLimit
+ commentId: P:PolylineAlgorithm.PolylineEncodingOptions.StackAllocLimit
+- markdown: Gets the maximum buffer size (in characters) that can be allocated on the stack for encoding operations.
+- code: public int StackAllocLimit { get; }
- h4: Property Value
- parameters:
- type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- h4: Remarks
-- markdown: The default maximum buffer size is 64,000 bytes (64 KB). This can be adjusted based on the expected size of the polyline data, but should be enough for common cases.
+- markdown: >-
+ When the required buffer size for encoding exceeds this limit, memory will be allocated on the heap instead of the stack.
+
+ This setting specifically applies to stack allocation of character arrays (stackalloc char[]) used during polyline encoding,
+
+ balancing performance and stack safety.
languageId: csharp
metadata:
- description: Options for configuring polyline encoding.
+ description: Provides configuration options for polyline encoding operations.
diff --git a/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml b/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml
index 91c4c2a6..673cd42f 100644
--- a/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml
+++ b/api-reference/1.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml
@@ -3,7 +3,7 @@ title: Class PolylineEncodingOptionsBuilder
body:
- api1: Class PolylineEncodingOptionsBuilder
id: PolylineAlgorithm_PolylineEncodingOptionsBuilder
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L15
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L15
metadata:
uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder
commentId: T:PolylineAlgorithm.PolylineEncodingOptionsBuilder
@@ -15,7 +15,7 @@ body:
- name: Assembly
value: PolylineAlgorithm.dll
- markdown: Provides a builder for configuring options for polyline encoding operations.
-- code: public class PolylineEncodingOptionsBuilder
+- code: public sealed class PolylineEncodingOptionsBuilder
- h4: Inheritance
- inheritance:
- text: object
@@ -32,8 +32,6 @@ body:
url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode
- text: object.GetType()
url: https://learn.microsoft.com/dotnet/api/system.object.gettype
- - text: object.MemberwiseClone()
- url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone
- text: object.ReferenceEquals(object, object)
url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
- text: object.ToString()
@@ -41,7 +39,7 @@ body:
- h2: Methods
- api3: Build()
id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_Build
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L37
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L38
metadata:
uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.Build
commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.Build
@@ -55,7 +53,7 @@ body:
description: A configured instance.
- api3: Create()
id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_Create
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L27
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L28
metadata:
uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.Create
commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.Create
@@ -69,11 +67,11 @@ body:
description: An instance for configuring polyline encoding options.
- api3: WithLoggerFactory(ILoggerFactory)
id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithLoggerFactory_Microsoft_Extensions_Logging_ILoggerFactory_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L72
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L97
metadata:
uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithLoggerFactory(Microsoft.Extensions.Logging.ILoggerFactory)
commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithLoggerFactory(Microsoft.Extensions.Logging.ILoggerFactory)
-- markdown: Sets the logger factory for logging during encoding operations.
+- markdown: Configures the to be used for logging during polyline encoding operations.
- code: public PolylineEncodingOptionsBuilder WithLoggerFactory(ILoggerFactory loggerFactory)
- h4: Parameters
- parameters:
@@ -81,46 +79,63 @@ body:
type:
- text: ILoggerFactory
url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerfactory
- description: The instance of a logger factory.
+ description: The instance to use for logging. If null , a will be used instead.
- h4: Returns
- parameters:
- type:
- text: PolylineEncodingOptionsBuilder
url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html
- description: The current builder instance.
-- h4: Exceptions
+ description: Returns the current instance for method chaining.
+- api3: WithPrecision(uint)
+ id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithPrecision_System_UInt32_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L82
+ metadata:
+ uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithPrecision(System.UInt32)
+ commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithPrecision(System.UInt32)
+- markdown: Sets the precision for encoding values.
+- code: public PolylineEncodingOptionsBuilder WithPrecision(uint precision)
+- h4: Parameters
+- parameters:
+ - name: precision
+ type:
+ - text: uint
+ url: https://learn.microsoft.com/dotnet/api/system.uint32
+ description: The number of decimal places to use for encoding values. Default is 5.
+- h4: Returns
- parameters:
- type:
- - text: ArgumentNullException
- url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception
- description: Thrown when loggerFactory is null .
-- api3: WithMaxBufferSize(int)
- id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithMaxBufferSize_System_Int32_
- src: https://github.com/petesramek/polyline-algorithm-csharp/blob/preview/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L54
+ - text: PolylineEncodingOptionsBuilder
+ url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html
+ description: The current builder instance.
+- api3: WithStackAllocLimit(int)
+ id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithStackAllocLimit_System_Int32_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L61
metadata:
- uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithMaxBufferSize(System.Int32)
- commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithMaxBufferSize(System.Int32)
-- markdown: Sets the buffer size for encoding operations.
-- code: public PolylineEncodingOptionsBuilder WithMaxBufferSize(int bufferSize)
+ uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithStackAllocLimit(System.Int32)
+ commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithStackAllocLimit(System.Int32)
+- markdown: Configures the buffer size used for stack allocation during polyline encoding operations.
+- code: public PolylineEncodingOptionsBuilder WithStackAllocLimit(int stackAllocLimit)
- h4: Parameters
- parameters:
- - name: bufferSize
+ - name: stackAllocLimit
type:
- text: int
url: https://learn.microsoft.com/dotnet/api/system.int32
- description: The maximum buffer size. Must be greater than 11.
+ description: The maximum buffer size to use for stack allocation. Must be greater than or equal to 1.
- h4: Returns
- parameters:
- type:
- text: PolylineEncodingOptionsBuilder
url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html
- description: The current builder instance.
+ description: Returns the current instance for method chaining.
+- h4: Remarks
+- markdown: This method allows customization of the internal buffer size for encoding, which can impact performance and memory usage.
- h4: Exceptions
- parameters:
- type:
- text: ArgumentOutOfRangeException
url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception
- description: Thrown when bufferSize is less than or equal to 11.
+ description: Thrown if stackAllocLimit is less than 1.
languageId: csharp
metadata:
description: Provides a builder for configuring options for polyline encoding operations.
diff --git a/api-reference/1.0/PolylineAlgorithm.Pow10.yml b/api-reference/1.0/PolylineAlgorithm.Pow10.yml
new file mode 100644
index 00000000..460a5871
--- /dev/null
+++ b/api-reference/1.0/PolylineAlgorithm.Pow10.yml
@@ -0,0 +1,104 @@
+### YamlMime:ApiPage
+title: Class Pow10
+body:
+- api1: Class Pow10
+ id: PolylineAlgorithm_Pow10
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Pow10.cs#L12
+ metadata:
+ uid: PolylineAlgorithm.Pow10
+ commentId: T:PolylineAlgorithm.Pow10
+- facts:
+ - name: Namespace
+ value:
+ text: PolylineAlgorithm
+ url: PolylineAlgorithm.html
+ - name: Assembly
+ value: PolylineAlgorithm.dll
+- markdown: Provides optimized calculation of powers of 10 for precision-based operations.
+- code: public static class Pow10
+- h4: Inheritance
+- inheritance:
+ - text: object
+ url: https://learn.microsoft.com/dotnet/api/system.object
+ - text: Pow10
+ url: PolylineAlgorithm.Pow10.html
+- h4: Inherited Members
+- list:
+ - text: object.Equals(object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)
+ - text: object.Equals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)
+ - text: object.GetHashCode()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode
+ - text: object.GetType()
+ url: https://learn.microsoft.com/dotnet/api/system.object.gettype
+ - text: object.MemberwiseClone()
+ url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone
+ - text: object.ReferenceEquals(object, object)
+ url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals
+ - text: object.ToString()
+ url: https://learn.microsoft.com/dotnet/api/system.object.tostring
+- h2: Remarks
+- markdown: >-
+ This class caches common powers of 10 (10^0 through 10^9) for efficient lookup,
+
+ falling back to for larger exponents.
+- h2: Properties
+- api3: UseCache
+ id: PolylineAlgorithm_Pow10_UseCache
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Pow10.cs#L29
+ metadata:
+ uid: PolylineAlgorithm.Pow10.UseCache
+ commentId: P:PolylineAlgorithm.Pow10.UseCache
+- markdown: Gets or sets a value indicating whether pre-computed powers of 10 should be used for optimization.
+- code: public static bool UseCache { get; set; }
+- h4: Property Value
+- parameters:
+ - type:
+ - text: bool
+ url: https://learn.microsoft.com/dotnet/api/system.boolean
+- h4: Remarks
+- markdown: >-
+ When enabled, retrieves values from the cache for precision levels 0-9,
+
+ providing faster performance. When disabled, all calculations use .
+- h2: Methods
+- api3: GetFactor(uint)
+ id: PolylineAlgorithm_Pow10_GetFactor_System_UInt32_
+ src: https://github.com/petesramek/polyline-algorithm-csharp/blob/develop/1.0/src/PolylineAlgorithm/Pow10.cs#L49
+ metadata:
+ uid: PolylineAlgorithm.Pow10.GetFactor(System.UInt32)
+ commentId: M:PolylineAlgorithm.Pow10.GetFactor(System.UInt32)
+- markdown: Returns the power of 10 for the specified precision level.
+- code: public static uint GetFactor(uint precision)
+- h4: Parameters
+- parameters:
+ - name: precision
+ type:
+ - text: uint
+ url: https://learn.microsoft.com/dotnet/api/system.uint32
+ description: The exponent for the base 10 (i.e., the number of decimal places).
+- h4: Returns
+- parameters:
+ - type:
+ - text: uint
+ url: https://learn.microsoft.com/dotnet/api/system.uint32
+ description: The value of 10 raised to the power of precision as a
+- h4: Remarks
+- markdown: >-
+ If is true and precision is between 0 and 9 (inclusive),
+
+ the method returns a cached value for optimal performance. For other values or if caching is disabled,
+
+ the result is computed using . The calculation is performed in a checked context to
+
+ ensure that arithmetic overflow is detected.
+- h4: Exceptions
+- parameters:
+ - type:
+ - text: OverflowException
+ url: https://learn.microsoft.com/dotnet/api/system.overflowexception
+ description: Thrown if the computed value exceeds .
+languageId: csharp
+metadata:
+ description: Provides optimized calculation of powers of 10 for precision-based operations.
diff --git a/api-reference/1.0/PolylineAlgorithm.yml b/api-reference/1.0/PolylineAlgorithm.yml
index ace2f946..49e94637 100644
--- a/api-reference/1.0/PolylineAlgorithm.yml
+++ b/api-reference/1.0/PolylineAlgorithm.yml
@@ -11,15 +11,14 @@ body:
- type:
text: PolylineAlgorithm.Abstraction
url: PolylineAlgorithm.Abstraction.html
+ - type:
+ text: PolylineAlgorithm.Diagnostics
+ url: PolylineAlgorithm.Diagnostics.html
- type:
text: PolylineAlgorithm.Extensions
url: PolylineAlgorithm.Extensions.html
- h3: Classes
- parameters:
- - type:
- text: InvalidPolylineException
- url: PolylineAlgorithm.InvalidPolylineException.html
- description: Exception thrown when a polyline is determined to be malformed or invalid during processing.
- type:
text: PolylineDecoder
url: PolylineAlgorithm.PolylineDecoder.html
@@ -44,11 +43,15 @@ body:
- type:
text: PolylineEncodingOptions
url: PolylineAlgorithm.PolylineEncodingOptions.html
- description: Options for configuring polyline encoding.
+ description: Provides configuration options for polyline encoding operations.
- type:
text: PolylineEncodingOptionsBuilder
url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html
description: Provides a builder for configuring options for polyline encoding operations.
+ - type:
+ text: Coordinate.Validator
+ url: PolylineAlgorithm.Coordinate.Validator.html
+ description: Provides static methods for validating latitude and longitude values used in .
- h3: Structs
- parameters:
- type:
@@ -62,10 +65,4 @@ body:
Represents an immutable, read-only encoded polyline string.
Provides methods for creation, inspection, and conversion of polyline data from various character sources.
-- h3: Enums
-- parameters:
- - type:
- text: CoordinateValueType
- url: PolylineAlgorithm.CoordinateValueType.html
- description: Represents the type of a geographic coordinate value.
languageId: csharp
diff --git a/api-reference/1.0/toc.yml b/api-reference/1.0/toc.yml
index b69f5d3f..d9b212c0 100644
--- a/api-reference/1.0/toc.yml
+++ b/api-reference/1.0/toc.yml
@@ -3,8 +3,8 @@
href: PolylineAlgorithm.yml
items:
- name: Classes
- - name: InvalidPolylineException
- href: PolylineAlgorithm.InvalidPolylineException.yml
+ - name: Coordinate.Validator
+ href: PolylineAlgorithm.Coordinate.Validator.yml
- name: PolylineDecoder
href: PolylineAlgorithm.PolylineDecoder.yml
- name: PolylineEncoder
@@ -20,9 +20,6 @@
href: PolylineAlgorithm.Coordinate.yml
- name: Polyline
href: PolylineAlgorithm.Polyline.yml
- - name: Enums
- - name: CoordinateValueType
- href: PolylineAlgorithm.CoordinateValueType.yml
- name: PolylineAlgorithm.Abstraction
href: PolylineAlgorithm.Abstraction.yml
items:
@@ -36,6 +33,12 @@
href: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml
- name: IPolylineEncoder
href: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml
+- name: PolylineAlgorithm.Diagnostics
+ href: PolylineAlgorithm.Diagnostics.yml
+ items:
+ - name: Classes
+ - name: InvalidPolylineException
+ href: PolylineAlgorithm.Diagnostics.InvalidPolylineException.yml
- name: PolylineAlgorithm.Extensions
href: PolylineAlgorithm.Extensions.yml
items:
diff --git a/api-reference/guide/advanced-scenarios.md b/api-reference/guide/advanced-scenarios.md
new file mode 100644
index 00000000..e731319c
--- /dev/null
+++ b/api-reference/guide/advanced-scenarios.md
@@ -0,0 +1,168 @@
+# Advanced Usage
+
+PolylineAlgorithm is designed for extensibility and integration with advanced .NET scenarios.
+This guide covers custom types, integrations, and best practices for power users.
+
+---
+
+## Custom Coordinate and Polyline Types
+
+You can encode and decode custom coordinate or polyline representations by extending the abstract base classes:
+
+- `AbstractPolylineEncoder`
+- `AbstractPolylineDecoder`
+
+### Example: Custom Encoder
+
+```csharp
+public sealed class MyPolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string>
+{
+ public MyPolylineEncoder() : base() { }
+
+ public MyPolylineEncoder(PolylineEncodingOptions options)
+ : base(options) { }
+
+ protected override double GetLatitude((double Latitude, double Longitude) coordinate)
+ => coordinate.Latitude;
+
+ protected override double GetLongitude((double Latitude, double Longitude) coordinate)
+ => coordinate.Longitude;
+
+ protected override string CreatePolyline(ReadOnlyMemory polyline)
+ => polyline.ToString();
+}
+```
+
+---
+
+## Example: Custom Decoder
+
+```csharp
+public sealed class MyPolylineDecoder : AbstractPolylineDecoder
+{
+ public MyPolylineDecoder() : base() { }
+
+ public MyPolylineDecoder(PolylineEncodingOptions options)
+ : base(options) { }
+
+ protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude)
+ => (latitude, longitude);
+
+ protected override ReadOnlyMemory GetReadOnlyMemory(ref string polyline)
+ => polyline.AsMemory();
+}
+```
+
+---
+
+# Registering Custom Encoder and Decoder with `IServiceCollection`
+
+For ASP.NET Core or DI-enabled .NET applications, you can easily register your custom polyline encoder and decoder as services with `IServiceCollection` by defining an extension method. This enables constructor injection and central DI management.
+
+---
+
+## Example: Register Custom Polyline Encoder/Decoder
+
+Suppose you have the following custom encoder and decoder (see [Advanced Usage](./advanced.md)):
+
+```csharp
+public sealed class MyPolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string>
+{
+ public MyPolylineEncoder(PolylineEncodingOptions options = null)
+ : base(options) { }
+
+ // ... override required members ...
+}
+
+public sealed class MyPolylineDecoder : AbstractPolylineDecoder
+{
+ public MyPolylineDecoder(PolylineEncodingOptions options = null)
+ : base(options) { }
+
+ // ... override required members ...
+}
+```
+
+---
+
+## IServiceCollection Extension Method
+
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+using PolylineAlgorithm;
+
+public static class PolylineServiceCollectionExtensions
+{
+ public static IServiceCollection AddMyPolylineEncoderDecoder(
+ this IServiceCollection services,
+ PolylineEncodingOptions options = null)
+ {
+ // Register encoder and decoder as singletons (adjust lifetime as needed)
+ services.AddSingleton>(
+ _ => new MyPolylineEncoder(options));
+ services.AddSingleton>(
+ _ => new MyPolylineDecoder(options));
+ return services;
+ }
+}
+```
+
+---
+
+## Usage
+
+In your application startup (e.g., `Program.cs` or `Startup.cs`):
+
+```csharp
+using PolylineAlgorithm;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddMyPolylineEncoderDecoder(
+ PolylineEncodingOptionsBuilder.Create()
+ .SetMaxBufferSize(10000)
+ .Build()
+);
+
+// Now you can inject IPolylineEncoder<(double, double), string> and IPolylineDecoder
+```
+
+---
+
+## Benefits
+
+- **Central DI management** for polyline components
+- Plug-and-play integration with ASP.NET Core and modern .NET project styles
+- Easily swap out or configure encoders/decoders for different environments
+
+---
+
+> **Tip:**
+> You can generalize the extension method for different encoder/decoder types or include multiple algorithms by adding extra parameters.
+
+---
+
+## Integration Guidance
+
+- **Batch or incremental processing:**
+ For large datasets, control buffer sizes via `PolylineEncodingOptions`.
+- **Thread safety:**
+ Default encoders/decoders are stateless and thread-safe. If extending for mutable types, ensure synchronization.
+- **Logging:**
+ Integrate with .NET's `ILoggerFactory` when diagnostics or audit trails are needed.
+
+---
+
+## Best Practices
+
+- Always validate input data—leverage built-in validation or extend for custom rules.
+- Document all public APIs using XML comments for seamless integration with the auto-generated docs.
+- For non-standard coordinate systems or precision, clearly specify semantics in your custom encoder/decoder.
+
+---
+
+## More Resources
+
+- [Configuration](./configuration.md)
+- [FAQ](./faq.md)
+- [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
diff --git a/api-reference/guide/configuration.md b/api-reference/guide/configuration.md
new file mode 100644
index 00000000..4a155cb2
--- /dev/null
+++ b/api-reference/guide/configuration.md
@@ -0,0 +1,75 @@
+# Configuration
+
+PolylineAlgorithm offers flexible configuration for encoding and decoding polylines, allowing you to fine-tune performance, control validation, and integrate diagnostics and logging.
+
+---
+
+## PolylineEncodingOptions
+
+Most configuration is handled via the `PolylineEncodingOptions` object, which you can build using the fluent `PolylineEncodingOptionsBuilder`.
+
+### Example: Customizing Buffer Size
+
+```csharp
+using PolylineAlgorithm;
+
+var options = PolylineEncodingOptionsBuilder.Create()
+ .SetMaxBufferSize(10000) // Set custom buffer size
+ .Build();
+
+var encoder = new PolylineEncoder(options);
+```
+
+---
+
+## Logging and Diagnostics
+
+PolylineAlgorithm supports internal logging for advanced scenarios and diagnostic purposes.
+
+- Use your preferred .NET logging framework (`ILoggerFactory`)
+- Attach loggers for encoding/decoding diagnostics, especially in automated or agent-based environments
+
+---
+
+## Validation
+
+Input validation is always enabled by default:
+
+- Latitude: must be between -90 and 90
+- Longitude: must be between -180 and 180
+- Invalid or malformed coordinates throw descriptive exceptions
+
+For custom validation (e.g., for custom coordinate types), extend the provided interfaces or abstract base classes.
+
+---
+
+## Advanced Configuration Options
+
+When using `PolylineEncodingOptionsBuilder`, you may set:
+
+- **Buffer sizes:** Configure allocation for large or streaming polylines
+- **Logging hooks:** Integrate your logger for troubleshooting/instrumentation
+- **(Future)** Custom precision, additional metadata (as needed)
+
+See the XML API documentation for all available builder methods.
+
+---
+
+## Example: Full Custom Encoder with Options
+
+```csharp
+var options = PolylineEncodingOptionsBuilder.Create()
+ .SetMaxBufferSize(5000)
+ // .EnableLogging(myLogger) // (if applicable)
+ .Build();
+
+var encoder = new PolylineEncoder(options);
+```
+
+---
+
+## Further Reading
+
+- [Getting Started Guide](./guide.md)
+- [Advanced Usage](./advanced.md)
+- [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
diff --git a/api-reference/guide/faq.md b/api-reference/guide/faq.md
new file mode 100644
index 00000000..186f97aa
--- /dev/null
+++ b/api-reference/guide/faq.md
@@ -0,0 +1,63 @@
+# FAQ
+
+Frequently Asked Questions for PolylineAlgorithm
+
+---
+
+## General
+
+**Q: What coordinate ranges are valid?**
+A: Latitude must be between -90 and 90; longitude must be between -180 and 180. Passing out-of-range values throws `ArgumentOutOfRangeException`.
+
+**Q: Which .NET versions are supported?**
+A: Any platform supporting `netstandard2.1`, including .NET Core, .NET 5+, Xamarin, Unity, and Blazor.
+
+**Q: Can the library be used in Unity, Xamarin, Blazor, or other .NET-compatible platforms?**
+A: Yes! Any environment that supports `netstandard2.1` can use this library.
+
+---
+
+## Usage & Extensibility
+
+**Q: How do I add a new polyline algorithm or coordinate type?**
+A: Implement your own encoder/decoder using `AbstractPolylineEncoder` and `AbstractPolylineDecoder`. Add unit tests and XML doc comments, then submit a PR.
+
+**Q: How do I customize encoding options (e.g., buffer size, logging)?**
+A: Use `PolylineEncodingOptionsBuilder` to set options, and pass the result to the encoder or decoder constructor.
+
+**Q: Is the library thread-safe?**
+A: Yes, main encoding/decoding APIs are stateless and thread-safe. If you extend using shared mutable resources, ensure proper synchronization.
+
+**Q: What happens if I pass invalid or malformed input to the decoder?**
+A: The decoder throws descriptive exceptions for malformed polyline strings. Ensure proper exception handling in your application.
+
+**Q: Does the library support streaming or incremental decoding of polylines?**
+A: Currently, only batch encode/decode is supported. For streaming scenarios, implement your own logic using `PolylineEncoding` utilities.
+
+---
+
+## Features & Support
+
+**Q: Is there support for elevation, timestamps, or third coordinate values?**
+A: Not currently, and not planned for the core library. You may implement your own encoder/decoder using `PolylineEncoding` methods for extended coordinate data.
+
+**Q: How do I contribute documentation improvements?**
+A: Update XML doc comments in the codebase and submit a pull request. To improve guides, update relevant markdown files in `/api-reference/guide`.
+
+**Q: Where can I report bugs or request features?**
+A: Open a GitHub issue using the provided templates and tag `@petesramek`.
+
+---
+
+## Documentation & Community
+
+**Q: Where can I find detailed API documentation?**
+A: [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
+
+**Q: How do I contribute?**
+A: Read [CONTRIBUTING.md](../CONTRIBUTING.md), follow coding style and testing guidelines, and use issue/PR templates.
+
+**Q: Need more help?**
+A: Open an issue in the [GitHub repository](https://github.com/petesramek/polyline-algorithm-csharp/issues).
+
+---
diff --git a/api-reference/guide/getting-started.md b/api-reference/guide/getting-started.md
index 8b3a7945..54d558c4 100644
--- a/api-reference/guide/getting-started.md
+++ b/api-reference/guide/getting-started.md
@@ -1 +1,76 @@
-# Getting Started
\ No newline at end of file
+# Getting Started
+
+PolylineAlgorithm is a lightweight, Google-compliant polyline encoding/decoding library for .NET Standard 2.1 and above.
+Follow these simple steps to get started, encode and decode polylines, and configure advanced features.
+
+---
+
+## Installation
+
+Install via the .NET CLI:
+
+```shell
+dotnet add package PolylineAlgorithm
+```
+
+Or via NuGet Package Manager:
+
+```powershell
+Install-Package PolylineAlgorithm
+```
+
+---
+
+## Basic Usage
+
+### Encoding Coordinates
+
+```csharp
+using PolylineAlgorithm;
+
+var coordinates = new List
+{
+ new Coordinate(48.858370, 2.294481), // Eiffel Tower
+ new Coordinate(51.500729, -0.124625) // Big Ben
+};
+
+var encoder = new PolylineEncoder();
+Polyline encoded = encoder.Encode(coordinates);
+
+Console.WriteLine(encoded.ToString()); // Prints the encoded polyline string
+```
+
+### Decoding a Polyline
+
+```csharp
+using PolylineAlgorithm;
+
+var decoder = new PolylineDecoder();
+Polyline polyline = Polyline.FromString("yseiHoc_MwacOjnwM"); // Sample encoded string
+
+IEnumerable decoded = decoder.Decode(polyline);
+
+// Show decoded coordinates
+foreach(var coord in decoded)
+{
+ Console.WriteLine($"{coord.Latitude}, {coord.Longitude}");
+}
+```
+
+---
+
+## Customizing and Advanced Features
+
+- Use `PolylineEncodingOptionsBuilder` to customize settings (buffer size, logging, etc.)
+- Implement custom encoder/decoder types for advanced coordinate representations
+- See [API reference](https://petesramek.github.io/polyline-algorithm-csharp/) for details
+
+---
+
+## Need More Help?
+
+- [FAQ](./faq.md)
+- [Examples](./examples.md)
+- [Report an Issue](https://github.com/petesramek/polyline-algorithm-csharp/issues)
+
+---
diff --git a/api-reference/guide/introduction.md b/api-reference/guide/introduction.md
index f6ecaa67..a0874834 100644
--- a/api-reference/guide/introduction.md
+++ b/api-reference/guide/introduction.md
@@ -1 +1,33 @@
-# Introduction
\ No newline at end of file
+# Introduction
+
+Welcome to **PolylineAlgorithm for .NET**, a modern library offering Google-compliant polyline encoding and decoding with strong input validation, extensible API design, and robust performance.
+
+## What is PolylineAlgorithm?
+
+PolylineAlgorithm provides tools for encoding a sequence of geographic coordinates into a compact string format used by Google Maps and other mapping platforms, and for decoding those strings back into coordinates. Its simple API and thorough documentation make it suitable for .NET Core, .NET 5+, Xamarin, Unity, Blazor, and any framework supporting `netstandard2.1`.
+
+## Key Benefits
+
+- **Standards compliance**: Implements Google’s Encoded Polyline Algorithm as specified
+- **Immutable, strongly-typed objects**: Guarantees reliability and thread safety
+- **Extensible**: Custom coordinate/polyline support via abstract base classes and interfaces
+- **Robust input validation**: Throws descriptive exceptions for out-of-range or malformed input
+- **Configuration and logging**: Advanced options and integration with `ILoggerFactory`
+- **Full API documentation**: [Auto-generated API Reference](https://petesramek.github.io/polyline-algorithm-csharp/)
+- **Benchmarked and tested**: Includes unit and performance tests
+
+## Who Should Use This Library?
+
+- .NET developers needing polyline encoding/decoding in mapping, GIS, logistics, or spatial applications
+- Teams requiring reliability, easy integration, and customizable algorithms
+- Anyone seeking a lightweight, modern, and maintainable polyline solution
+
+## How To Get Started
+
+- See the [Getting Started Guide](./guide.md) for installation and basic usage
+- Explore [Examples](./examples.md) and [FAQ](./faq.md) for real-world scenarios and solutions
+- Read about advanced [configuration](./configuration.md) and [customization](./advanced.md)
+
+---
+
+For technical details, see the [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/).
diff --git a/api-reference/guide/sample.md b/api-reference/guide/sample.md
new file mode 100644
index 00000000..336bc254
--- /dev/null
+++ b/api-reference/guide/sample.md
@@ -0,0 +1,115 @@
+# Sample Console Application: Using NetTopologySuite with PolylineAlgorithm
+
+This sample demonstrates how to encode and decode polylines using custom implementations (`NetTopologyPolylineEncoder` and `NetTopologyPolylineDecoder`) based on NetTopologySuite's `Point` type.
+
+---
+
+## Prerequisites
+
+- Install the following NuGet packages:
+ - `PolylineAlgorithm`
+ - `NetTopologySuite`
+
+```shell
+dotnet add package PolylineAlgorithm
+dotnet add package NetTopologySuite
+```
+
+---
+
+## Program.cs
+
+```csharp
+using System;
+using System.Collections.Generic;
+using NetTopologySuite.Geometries;
+using PolylineAlgorithm.Abstraction;
+
+class Program
+{
+ static void Main()
+ {
+ // Create some sample points (latitude, longitude)
+ var points = new List
+ {
+ new Point(48.858370, 2.294481), // Eiffel Tower
+ new Point(51.500729, -0.124625) // Big Ben
+ };
+
+ // Instantiate the custom encoder
+ var encoder = new NetTopologyPolylineEncoder();
+
+ // Encode the list of points to a polyline string
+ string encodedPolyline = encoder.Encode(points);
+
+ Console.WriteLine("Encoded polyline string:");
+ Console.WriteLine(encodedPolyline);
+
+ // Instantiate the custom decoder
+ var decoder = new NetTopologyPolylineDecoder();
+
+ // Decode back to NetTopologySuite Point objects
+ IEnumerable decodedPoints = decoder.Decode(encodedPolyline);
+
+ Console.WriteLine("\nDecoded coordinates:");
+ foreach (var point in decodedPoints)
+ {
+ Console.WriteLine($"Latitude: {point.X}, Longitude: {point.Y}");
+ }
+ }
+}
+
+public sealed class NetTopologyPolylineDecoder : AbstractPolylineDecoder {
+ protected override Point CreateCoordinate(double latitude, double longitude) {
+ return new Point(latitude, longitude);
+ }
+
+ protected override ReadOnlyMemory GetReadOnlyMemory(ref string polyline) {
+ return polyline.AsMemory();
+ }
+}
+
+public sealed class NetTopologyPolylineEncoder : AbstractPolylineEncoder {
+ protected override string CreatePolyline(ReadOnlyMemory polyline) {
+ if (polyline.IsEmpty) {
+ return string.Empty;
+ }
+
+ return polyline.ToString();
+ }
+
+ protected override double GetLatitude(Point current) {
+ // Validate parameter
+
+ return current.X;
+ }
+
+ protected override double GetLongitude(Point current) {
+ // Validate parameter
+
+ return current.Y;
+ }
+}
+```
+
+---
+
+## Expected Output
+
+```text
+Encoded polyline string:
+{sample output will be generated at runtime}
+
+Decoded coordinates:
+Latitude: 48.85837, Longitude: 2.294481
+Latitude: 51.500729, Longitude: -0.124625
+```
+
+---
+
+## Notes
+
+- You can further extend this pattern to use any coordinate or geometry type supported by NetTopologySuite.
+- The sample demonstrates real usage of a custom `PolylineEncoder`/`PolylineDecoder` in a typical .NET application.
+
+---
diff --git a/api-reference/guide/toc.yml b/api-reference/guide/toc.yml
index d7e9ea8c..64357007 100644
--- a/api-reference/guide/toc.yml
+++ b/api-reference/guide/toc.yml
@@ -1,4 +1,12 @@
-- name: Introduction
+- name: Introduction
href: introduction.md
- name: Getting Started
- href: getting-started.md
\ No newline at end of file
+ href: getting-started.md
+- name: Configuration
+ href: configuration.md
+- name: Advanced Scenarios
+ href: advanced-scenarions.md
+- name: Sample
+ href: sample.md
+- name: FAQ
+ href: faq.md
diff --git a/api-reference/index.md b/api-reference/index.md
index 5403debe..8c5b8d16 100644
--- a/api-reference/index.md
+++ b/api-reference/index.md
@@ -1,26 +1,18 @@
-# PolylineAlgorithm for .NET
+# PolylineAlgorithm API Reference
-[](https://github.com/sramekpete/polyline-algorithm-csharp/actions/workflows/build.yml)
-[](https://www.nuget.org/packages/PolylineAlgorithm/)
-[](https://opensource.org/licenses/MIT)
+Welcome! This documentation provides guides, configuration options, examples, and FAQs for the PolylineAlgorithm library.
+For detailed class and method docs, see the [auto-generated API documentation](https://petesramek.github.io/polyline-algorithm-csharp/).
-Lightweight .NET Standard 2.1 library implementing Google Encoded Polyline Algorithm.
-More info about the algorithm can be found at [Google Developers](https://developers.google.com/maps/documentation/utilities/polylinealgorithm).
+## Contents
-## Prerequisites
+- [Quick Start Guide](./guide.md)
+- [Configuration Options](./configuration.md)
+- [Advanced Usage](./advanced.md)
+- [Examples](./examples.md)
+- [FAQ](./faq.md)
-PolylineAlgorithm for .NET is available as a NuGet package targeting .NET Standard 2.1.
+## Links
-**.NET CLI**
-
-`dotnet add package PolylineAlgorithm`
-
-**Package Manager Console**
-
-`Install-Package PolylineAlgorithm`
-
-## How to use it
-
-In the majority of cases you would like to inherit `AbstractPolylineDecoder` and `AbstractPolylineEncoder` classes and implement abstract methods that are mainly responsible for extracting data from your coordinate and polyline types and creating new instances of them.
-
-In some cases you may want to implement your own decoder and encoder from scratch. In that case you can use `PolylineEncoding` static class that offers static methods for encoding and decoding polyline segments.
\ No newline at end of file
+- [API Reference Site](https://petesramek.github.io/polyline-algorithm-csharp/)
+- [Contributing Guidelines](../CONTRIBUTING.md)
+- [Changelog](./changelog.md) (if provided)
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj
index 2859331b..a7c3ce4e 100644
--- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj
@@ -2,12 +2,12 @@
Exe
- net8.0;net9.0;net10.0;
- 13.0
- enable
- enable
- true
- en
+ net8.0;net9.0;net10.0
+
+
+
+ pdbonly
+ true
@@ -15,7 +15,13 @@
-
+
+
+
+
+
+
+
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineBenchmark.cs
index 9d09883b..8275f93e 100644
--- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineBenchmark.cs
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineBenchmark.cs
@@ -11,13 +11,16 @@ namespace PolylineAlgorithm.Benchmarks;
using PolylineAlgorithm.Utility;
///
-/// Benchmarks for the struct.
+/// Benchmarks for .
///
public class PolylineBenchmark {
- private static readonly Consumer consumer = new();
+ private static readonly Consumer _consumer = new();
+ ///
+ /// Number of coordinates for benchmarks. Set by BenchmarkDotNet.
+ ///
[Params(1, 100, 1_000)]
- public int Count;
+ public int CoordinatesCount { get; set; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
///
@@ -45,8 +48,12 @@ public class PolylineBenchmark {
///
public Polyline PolylineNotEqualValue { get; private set; }
+ ///
+ /// Gets the destination array used for benchmarking the method.
+ /// This array is initialized in to match the length of the encoded polyline,
+ /// and is used as the target buffer for copying polyline data during benchmark runs.
+ ///
public char[] CopyToDestination { get; private set; }
-
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
@@ -55,8 +62,8 @@ public class PolylineBenchmark {
///
[GlobalSetup]
public void SetupData() {
- PolylineValue = Polyline.FromString(RandomValueProvider.GetPolyline(Count));
- PolylineNotEqualValue = Polyline.FromString(RandomValueProvider.GetPolyline(Count + Random.Shared.Next(1, 101)));
+ PolylineValue = Polyline.FromString(RandomValueProvider.GetPolyline(CoordinatesCount));
+ PolylineNotEqualValue = Polyline.FromString(RandomValueProvider.GetPolyline(CoordinatesCount + Random.Shared.Next(1, 101)));
StringValue = PolylineValue.ToString();
CharArrayValue = [.. StringValue];
MemoryValue = CharArrayValue.AsMemory();
@@ -65,101 +72,101 @@ public void SetupData() {
}
///
- /// Benchmarks the encoding of a list of coordinates into a polyline.
+ /// Benchmark: create polyline from string.
///
/// The encoded polyline.
[Benchmark]
- public Polyline Polyline_FromString() {
+ public void Polyline_FromString() {
var polyline = Polyline
- .FromString(StringValue);
+ .FromString(StringValue);
- return polyline;
+ _consumer.Consume(polyline);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: create polyline from char array.
///
/// The encoded polyline.
[Benchmark]
- public Polyline Polyline_FromCharArray() {
+ public void Polyline_FromCharArray() {
var polyline = Polyline
- .FromCharArray(CharArrayValue);
+ .FromCharArray(CharArrayValue);
- return polyline;
+ _consumer.Consume(polyline);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: create polyline from memory.
///
/// The encoded polyline.
[Benchmark]
- public Polyline Polyline_FromMemory() {
+ public void Polyline_FromMemory() {
var polyline = Polyline
- .FromMemory(MemoryValue);
+ .FromMemory(MemoryValue);
- return polyline;
+ _consumer.Consume(polyline);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: convert polyline to string.
///
/// The encoded polyline.
[Benchmark]
- public string Polyline_ToString() {
+ public void Polyline_ToString() {
var stringValue = PolylineValue
- .ToString();
+ .ToString();
- return stringValue;
+ _consumer.Consume(stringValue);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: copy polyline to array.
///
/// The encoded polyline.
[Benchmark]
public void Polyline_CopyTo() {
PolylineValue
- .CopyTo(CopyToDestination);
+ .CopyTo(CopyToDestination);
CopyToDestination
- .Consume(consumer);
+ .Consume(_consumer);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: compare polyline with same value.
///
/// The encoded polyline.
[Benchmark]
- public bool Polyline_Equals_SameValue() {
+ public void Polyline_Equals_SameValue() {
var equals = PolylineValue
- .Equals(PolylineValue);
+ .Equals(PolylineValue);
- return equals;
+ _consumer.Consume(equals);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: compare polyline with different value.
///
/// The encoded polyline.
[Benchmark]
- public bool Polyline_Equals_DifferentValue() {
+ public void Polyline_Equals_DifferentValue() {
var equals = PolylineValue
- .Equals(PolylineNotEqualValue);
+ .Equals(PolylineNotEqualValue);
- return equals;
+ _consumer.Consume(equals);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: compare polyline with different type.
///
/// The encoded polyline.
[Benchmark]
- public bool Polyline_Equals_DifferentType() {
+ public void Polyline_Equals_DifferentType() {
var equals = PolylineValue
- .Equals(StringValue);
+ .Equals(StringValue);
- return equals;
+ _consumer.Consume(equals);
}
}
\ No newline at end of file
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs
index 718cdd8b..fc8ba1e9 100644
--- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs
@@ -8,45 +8,94 @@ namespace PolylineAlgorithm.Benchmarks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using PolylineAlgorithm;
+using PolylineAlgorithm.Extensions;
using PolylineAlgorithm.Utility;
///
-/// Benchmarks for the class.
+/// Benchmarks for .
///
public class PolylineDecoderBenchmark {
private readonly Consumer _consumer = new();
[Params(1, 100, 1_000)]
- public int Count;
+ public int CoordinatesCount { get; set; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
///
- /// Gets the string value representing the encoded polyline.
+ /// Polyline instance for benchmarks.
///
public Polyline Polyline { get; private set; }
+ ///
+ /// Encoded polyline as string.
+ ///
+ public string String { get; private set; }
+
+ ///
+ /// Encoded polyline as char array.
+ ///
+ public char[] CharArray { get; private set; }
+
+ ///
+ /// Encoded polyline as read-only memory.
+ ///
+ public ReadOnlyMemory Memory { get; private set; }
+
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
///
- /// The polyline decoder instance.
+ /// Polyline decoder instance.
///
- public PolylineDecoder Decoder = new();
+ private readonly PolylineDecoder _decoder = new();
///
- /// Sets up the data for the benchmarks.
+ /// Sets up benchmark data.
///
[GlobalSetup]
public void SetupData() {
- Polyline = Polyline.FromString(RandomValueProvider.GetPolyline(Count));
+ Polyline = Polyline.FromString(RandomValueProvider.GetPolyline(CoordinatesCount));
+ String = RandomValueProvider.GetPolyline(CoordinatesCount);
+ CharArray = RandomValueProvider.GetPolyline(CoordinatesCount).ToCharArray();
+ Memory = RandomValueProvider.GetPolyline(CoordinatesCount).AsMemory();
}
///
- /// Benchmarks the decoding of a polyline from a string.
+ /// Benchmark: decode polyline instance.
///
[Benchmark]
- public void PolylineDecoder_Decode() {
- Decoder
+ public void PolylineDecoder_Decode_Polyline() {
+ _decoder
.Decode(Polyline)
.Consume(_consumer);
}
+
+ ///
+ /// Benchmark: decode from string.
+ ///
+ [Benchmark]
+ public void PolylineDecoder_Decode_String() {
+ _decoder
+ .Decode(String)
+ .Consume(_consumer);
+ }
+
+ ///
+ /// Benchmark: decode from char array.
+ ///
+ [Benchmark]
+ public void PolylineDecoder_Decode_CharArray() {
+ _decoder
+ .Decode(CharArray)
+ .Consume(_consumer);
+ }
+
+ ///
+ /// Benchmark: decode from memory.
+ ///
+ [Benchmark]
+ public void PolylineDecoder_Decode_Memory() {
+ _decoder
+ .Decode(Memory)
+ .Consume(_consumer);
+ }
}
\ No newline at end of file
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs
index 71743ca8..3993ecf8 100644
--- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs
@@ -6,64 +6,90 @@
namespace PolylineAlgorithm.Benchmarks;
using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Engines;
using PolylineAlgorithm;
+using PolylineAlgorithm.Extensions;
using PolylineAlgorithm.Utility;
using System.Collections.Generic;
///
-/// Benchmarks for the class.
+/// Benchmarks for .
///
public class PolylineEncoderBenchmark {
+ private readonly Consumer _consumer = new();
+
+ ///
+ /// Number of coordinates for benchmarks.
+ ///
[Params(1, 100, 1_000)]
- public int Count;
+ public int CoordinatesCount { get; set; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
///
- /// Gets the enumeration of coordinates to be encoded.
+ /// Coordinates as list.
///
- public IEnumerable Enumeration { get; private set; }
+ public List List { get; private set; }
///
- /// Gets the list of coordinates to be encoded.
+ /// Coordinates as array.
///
- public List List { get; private set; }
+ public Coordinate[] Array { get; private set; }
+
+ ///
+ /// Coordinates as read-only memory.
+ ///
+ public ReadOnlyMemory Memory { get; private set; }
+
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
///
- /// The polyline encoder instance.
+ /// Polyline encoder instance.
///
- public PolylineEncoder Encoder = new();
+ private readonly PolylineEncoder _encoder = new();
///
- /// Sets up the data for the benchmarks.
+ /// Sets up benchmark data.
///
[GlobalSetup]
public void SetupData() {
- Enumeration = RandomValueProvider.GetCoordinates(Count).Select(c => new Coordinate(c.Latitude, c.Longitude));
- List = [.. Enumeration];
+ List = [.. RandomValueProvider.GetCoordinates(CoordinatesCount).Select(c => new Coordinate(c.Latitude, c.Longitude))];
+ Array = [.. List];
+ Memory = Array.AsMemory();
}
///
- /// Benchmarks the encoding of a list of coordinates into a polyline.
+ /// Benchmark: encode coordinates from span.
///
- /// The encoded polyline.
+ /// Encoded polyline.
[Benchmark]
- public Polyline PolylineEncoder_Encode_List() {
- var polyline = Encoder
- .Encode(List!);
+ public void PolylineEncoder_Encode_Span() {
+ var polyline = _encoder
+ .Encode(Memory.Span!);
+
+ _consumer.Consume(polyline);
+ }
+
+ ///
+ /// Benchmark: encode coordinates from array.
+ ///
+ /// Encoded polyline.
+ [Benchmark]
+ public void PolylineEncoder_Encode_Array() {
+ var polyline = _encoder
+ .Encode(Array!);
- return polyline;
+ _consumer.Consume(polyline);
}
///
- /// Benchmarks the encoding of an enumeration of coordinates into a polyline.
+ /// Benchmark: encode coordinates from list.
///
- /// The encoded polyline.
+ /// Encoded polyline.
[Benchmark]
- public Polyline PolylineEncoder_Encode_Enumerator() {
- var polyline = Encoder
- .Encode(Enumeration!);
+ public void PolylineEncoder_Encode_List() {
+ var polyline = _encoder
+ .Encode(List!);
- return polyline;
+ _consumer.Consume(polyline);
}
}
\ No newline at end of file
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncodingValidationBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncodingValidationBenchmark.cs
new file mode 100644
index 00000000..42d3c349
--- /dev/null
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncodingValidationBenchmark.cs
@@ -0,0 +1,29 @@
+namespace PolylineAlgorithm.Benchmarks;
+
+using BenchmarkDotNet.Attributes;
+using PolylineAlgorithm.Utility;
+
+public class PolylineEncodingValidationBenchmark {
+ private string polyline;
+
+ ///
+ /// Number of coordinates for benchmarks. Set by BenchmarkDotNet.
+ ///
+ [Params(8, 64, 128, 1024, 4096, 20480, 102400)]
+ public int CoordinatesCount { get; set; }
+
+ [GlobalSetup]
+ public void Setup() {
+ polyline = RandomValueProvider.GetPolyline(CoordinatesCount);
+ }
+
+ [Benchmark(Baseline = true)]
+ public void ValidateCharRange() => PolylineEncoding.ValidateCharRange(polyline);
+
+ [Benchmark]
+ public void ValidateBlockLength() => PolylineEncoding.ValidateBlockLength(polyline);
+
+
+ [Benchmark]
+ public void ValidateFormat() => PolylineEncoding.ValidateFormat(polyline);
+}
\ No newline at end of file
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/Program.cs b/benchmarks/PolylineAlgorithm.Benchmarks/Program.cs
index e1dffccb..92c38c10 100644
--- a/benchmarks/PolylineAlgorithm.Benchmarks/Program.cs
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/Program.cs
@@ -8,13 +8,13 @@ namespace PolylineAlgorithm.Benchmarks;
using BenchmarkDotNet.Running;
///
-/// The main entry point for the benchmark application.
+/// Main entry point for benchmarks.
///
-internal class Program {
+internal static class Program {
///
- /// The main method that runs the benchmarks.
+ /// Runs the benchmarks.
///
- /// The command-line arguments.
+ /// Command-line arguments.
static void Main(string[] args) {
BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/Properties/CodeCoverage.cs b/benchmarks/PolylineAlgorithm.Benchmarks/Properties/CodeCoverage.cs
new file mode 100644
index 00000000..04094932
--- /dev/null
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/Properties/CodeCoverage.cs
@@ -0,0 +1,8 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: ExcludeFromCodeCoverage]
\ No newline at end of file
diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/Properties/GlobalSuppressions.cs b/benchmarks/PolylineAlgorithm.Benchmarks/Properties/GlobalSuppressions.cs
index 39178369..98b7cc75 100644
--- a/benchmarks/PolylineAlgorithm.Benchmarks/Properties/GlobalSuppressions.cs
+++ b/benchmarks/PolylineAlgorithm.Benchmarks/Properties/GlobalSuppressions.cs
@@ -1,8 +1,15 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
-[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Benchmarks need instance methods.")]
\ No newline at end of file
+[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Design", "MA0016:Prefer using collection abstraction instead of implementation", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "Benchmarks.")]
+[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Benchmarks need instance methods.")]
diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs
index 10a034d0..8e6fcc68 100644
--- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs
+++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs
@@ -10,38 +10,26 @@ namespace PolylineAlgorithm.NetTopologySuite.Sample;
using System;
///
-/// Represents a polyline decoder that converts encoded polyline strings into a collection of geographic coordinates using NetTopologySuite.
+/// Polyline decoder using NetTopologySuite.
///
public sealed class NetTopologyPolylineDecoder : AbstractPolylineDecoder {
///
- /// Creates a coordinate instance from the given latitude and longitude values.
+ /// Creates a NetTopologySuite point from latitude and longitude.
///
- ///
- /// The latitude value.
- ///
- ///
- /// The longitude value.
- ///
- ///
- /// A coordinate instance of type .
- ///
+ /// Latitude value.
+ /// Longitude value.
+ /// Point instance.
protected override Point CreateCoordinate(double latitude, double longitude) {
- return new Point(latitude, longitude);
+ // NetTopologySuite Point: x = longitude, y = latitude
+ return new Point(longitude, latitude);
}
///
- /// Converts the provided polyline string into a read-only memory of characters.
+ /// Converts polyline string to read-only memory.
///
- ///
- /// The encoded polyline string to be converted into a read-only memory of characters.
- ///
- ///
- /// A containing the characters of the polyline string.
- ///
- ///
- /// Thrown when the provided polyline string is null, empty, or consists only of whitespace characters.
- ///
- protected override ReadOnlyMemory GetReadOnlyMemory(string polyline) {
+ /// Encoded polyline string.
+ /// ReadOnlyMemory of characters.
+ protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) {
return polyline.AsMemory();
}
}
diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs
index f9e71f50..d829d27c 100644
--- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs
+++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs
@@ -9,18 +9,14 @@ namespace PolylineAlgorithm.NetTopologySuite.Sample;
using PolylineAlgorithm.Abstraction;
///
-/// Encodes a collection of geographic coordinates into an encoded polyline string using NetTopologySuite's Point type.
+/// Polyline encoder using NetTopologySuite's Point type.
///
public sealed class NetTopologyPolylineEncoder : AbstractPolylineEncoder {
///
- /// Creates a string representation of the provided polyline.
+ /// Creates encoded polyline string from memory.
///
- ///
- /// The polyline to encode as a string.
- ///
- ///
- /// An encoded polyline string representation of the provided polyline.
- ///
+ /// Polyline memory.
+ /// Encoded polyline string.
protected override string CreatePolyline(ReadOnlyMemory polyline) {
if (polyline.IsEmpty) {
return string.Empty;
@@ -30,31 +26,27 @@ protected override string CreatePolyline(ReadOnlyMemory polyline) {
}
///
- /// Extracts the latitude value from the specified coordinate.
+ /// Gets latitude from point.
///
- ///
- /// The coordinate from which to extract the latitude value. This should be a not null instance.
- ///
- ///
- /// The latitude value as a .
- ///
+ /// Point instance.
+ /// Latitude value.
protected override double GetLatitude(Point current) {
- // Validate parameter
+ if (current is null) {
+ throw new ArgumentNullException(nameof(current));
+ }
return current.X;
}
///
- /// Extracts the longitude value from the specified coordinate.
+ /// Gets longitude from point.
///
- ///
- /// The coordinate from which to extract the longitude value. This should be a not null instance.
- ///
- ///
- /// The longitude value as a .
- ///
+ /// Point instance.
+ /// Longitude value.
protected override double GetLongitude(Point current) {
- // Validate parameter
+ if (current is null) {
+ throw new ArgumentNullException(nameof(current));
+ }
return current.Y;
}
diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/PolylineAlgorithm.NetTopologySuite.Sample.csproj b/samples/PolylineAlgorithm.NetTopologySuite.Sample/PolylineAlgorithm.NetTopologySuite.Sample.csproj
index 6aad5fb7..57d5f7c1 100644
--- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/PolylineAlgorithm.NetTopologySuite.Sample.csproj
+++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/PolylineAlgorithm.NetTopologySuite.Sample.csproj
@@ -2,18 +2,6 @@
netstandard2.1
- 13.0
- enable
- enable
- true
- en
-
-
-
- All
- latest
- true
- true
@@ -21,11 +9,11 @@
-
+
-
\ No newline at end of file
+
diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/Properties/CodeCoverage.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/Properties/CodeCoverage.cs
new file mode 100644
index 00000000..04094932
--- /dev/null
+++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/Properties/CodeCoverage.cs
@@ -0,0 +1,8 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: ExcludeFromCodeCoverage]
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs
index b15766b5..12c4a447 100644
--- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs
+++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs
@@ -3,152 +3,181 @@
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
-namespace PolylineAlgorithm.Abstraction;
-
using Microsoft.Extensions.Logging;
-using PolylineAlgorithm;
+using PolylineAlgorithm.Diagnostics;
using PolylineAlgorithm.Internal;
-using PolylineAlgorithm.Internal.Logging;
-using PolylineAlgorithm.Properties;
-using System;
-using System.Diagnostics.CodeAnalysis;
-
-///
-/// Decodes encoded polyline strings into sequences of geographic coordinates.
-/// Implements the interface.
-///
-///
-/// This abstract class provides a base implementation for decoding polylines, allowing subclasses to define how to handle specific polyline formats.
-///
-public abstract class AbstractPolylineDecoder : IPolylineDecoder {
- ///
- /// Initializes a new instance of the class with default encoding options.
- ///
- protected AbstractPolylineDecoder()
- : this(new PolylineEncodingOptions()) { }
-
- ///
- /// Initializes a new instance of the class with the specified encoding options.
- ///
- ///
- /// The to use for encoding operations.
- ///
- ///
- /// Thrown when is
- ///
- protected AbstractPolylineDecoder(PolylineEncodingOptions options) {
- Options = options ?? throw new ArgumentNullException(nameof(options));
- }
-
- ///
- /// Gets the encoding options used by this polyline encoder.
- ///
- public PolylineEncodingOptions Options { get; }
+using PolylineAlgorithm.Internal.Diagnostics;
+using PolylineAlgorithm.Internal.Diagnostics;
+using System.Runtime.CompilerServices;
+namespace PolylineAlgorithm.Abstraction {
///
- /// Decodes an encoded into a sequence of instances.
+ /// Decodes encoded polyline strings into sequences of geographic coordinates.
+ /// Implements the interface.
///
- ///
- /// The instance containing the encoded polyline string to decode.
- ///
- ///
- /// An of representing the decoded latitude and longitude pairs.
- ///
- ///
- /// Thrown when is .
- /// "
- ///
- /// Thrown when is empty.
- ///
- ///
- /// Thrown when the polyline format is invalid or malformed at a specific position.
- ///
- public IEnumerable Decode(TPolyline polyline) {
- var logger = Options
- .LoggerFactory
- .CreateLogger>();
-
- logger.
- LogOperationStartedInfo(nameof(Decode));
-
- ValidateNullPolyline(polyline, logger);
-
- ReadOnlyMemory sequence = GetReadOnlyMemory(polyline);
-
- ValidateEmptySequence(logger, sequence);
-
- int position = 0;
- int latitude = 0;
- int longitude = 0;
-
- while (true) {
- // Check if we have reached the end of the sequence
- if (sequence.Length == position) {
- break;
+ ///
+ /// This abstract class provides a base implementation for decoding polylines, allowing subclasses to define how to handle specific polyline formats.
+ ///
+ public abstract class AbstractPolylineDecoder : IPolylineDecoder {
+ private readonly ILogger> _logger;
+
+ ///
+ /// Initializes a new instance of the class with default encoding options.
+ ///
+ protected AbstractPolylineDecoder()
+ : this(new PolylineEncodingOptions()) { }
+
+ ///
+ /// Initializes a new instance of the class with the specified encoding options.
+ ///
+ ///
+ /// The to use for encoding operations.
+ ///
+ ///
+ /// Thrown when is
+ ///
+ protected AbstractPolylineDecoder(PolylineEncodingOptions options) {
+ if (options is null) {
+ ExceptionGuard.ThrowArgumentNull(nameof(options));
}
- // Read the next value from the polyline encoding
- if (!PolylineEncoding.TryReadValue(ref latitude, ref sequence, ref position)
- || !PolylineEncoding.TryReadValue(ref longitude, ref sequence, ref position)
- ) {
- logger
- .LogInvalidPolylineWarning(position);
- logger.
- LogOperationFailedInfo(nameof(Decode));
+ Options = options;
+ _logger = Options
+ .LoggerFactory
+ .CreateLogger>();
+ }
- InvalidPolylineException.Throw(position);
+ ///
+ /// Gets the encoding options used by this polyline decoder.
+ ///
+ public PolylineEncodingOptions Options { get; }
+
+ ///
+ /// Decodes an encoded into a sequence of instances.
+ ///
+ ///
+ /// The instance containing the encoded polyline string to decode.
+ ///
+ ///
+ /// An of representing the decoded latitude and longitude pairs.
+ ///
+ ///
+ /// Thrown when is .
+ ///
+ ///
+ /// Thrown when is empty.
+ ///
+ ///
+ /// Thrown when the polyline format is invalid or malformed at a specific position.
+ ///
+ public IEnumerable Decode(TPolyline polyline)
+ => Decode(polyline, CancellationToken.None);
+
+ ///
+ /// Decodes an encoded polyline with cancellation support.
+ ///
+ /// The encoded polyline.
+ /// Cancellation token.
+ /// Decoded coordinates.
+ ///
+ ///
+ ///
+ public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken) {
+ const string OperationName = nameof(Decode);
+
+ _logger?.LogOperationStartedDebug(OperationName);
+
+ ValidateNullPolyline(polyline, _logger);
+
+ ReadOnlyMemory sequence = GetReadOnlyMemory(in polyline);
+
+ ValidateSequence(sequence, _logger);
+ ValidateFormat(sequence, _logger);
+
+ int position = 0;
+ int encodedLatitude = 0;
+ int encodedLongitude = 0;
+
+ try {
+ while (position < sequence.Length) {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (!PolylineEncoding.TryReadValue(ref encodedLatitude, sequence, ref position)
+ || !PolylineEncoding.TryReadValue(ref encodedLongitude, sequence, ref position)) {
+ _logger?.LogOperationFailedDebug(OperationName);
+ _logger?.LogInvalidPolylineWarning(position);
+
+ ExceptionGuard.ThrowInvalidPolylineFormat(position);
+ }
+
+ double decodedLatitude = PolylineEncoding.Denormalize(encodedLatitude, Options.Precision);
+ double decodedLongitude = PolylineEncoding.Denormalize(encodedLongitude, Options.Precision);
+
+ _logger?.LogDecodedCoordinateDebug(decodedLatitude, decodedLongitude, position);
+
+ yield return CreateCoordinate(decodedLatitude, decodedLongitude);
+ }
+ } finally {
+ _logger?.LogOperationFinishedDebug(OperationName);
}
-
- yield return CreateCoordinate(PolylineEncoding.Denormalize(latitude, CoordinateValueType.Latitude), PolylineEncoding.Denormalize(longitude, CoordinateValueType.Longitude));
}
- logger
- .LogOperationFinishedInfo(nameof(Decode));
-
-
- static void ValidateNullPolyline(TPolyline polyline, ILogger> logger) {
+ ///
+ /// Validates that the provided polyline is not .
+ /// Throws an if the polyline is .
+ /// Optionally logs a warning if a logger is provided.
+ ///
+ /// The polyline instance to validate.
+ /// Optional logger for diagnostic messages.
+ ///
+ /// Thrown when is .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void ValidateNullPolyline(TPolyline polyline, ILogger? logger) {
if (polyline is null) {
- logger
- .LogNullArgumentWarning(nameof(polyline));
-
- throw new ArgumentNullException(nameof(polyline));
+ logger?.LogNullArgumentWarning(nameof(polyline));
+ ExceptionGuard.ThrowArgumentNull(nameof(polyline));
}
}
- static void ValidateEmptySequence(ILogger> logger, ReadOnlyMemory sequence) {
- if (sequence.Length < Defaults.Polyline.Block.Length.Min) {
- logger
- .LogPolylineCannotBeShorterThanWarning(nameof(sequence), sequence.Length, Defaults.Polyline.Block.Length.Min);
- logger.
- LogOperationFailedInfo(nameof(Decode));
+ ///
+ /// Validates that the polyline sequence meets the minimum required length.
+ /// Throws an if the sequence is too short.
+ /// Optionally logs diagnostic messages if a logger is provided.
+ ///
+ /// The polyline character sequence to validate.
+ /// Optional logger for diagnostic messages.
+ ///
+ /// Thrown when is shorter than the minimum allowed length.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void ValidateSequence(ReadOnlyMemory polylineSequence, ILogger? logger) {
+ if (polylineSequence.Length < Defaults.Polyline.Block.Length.Min) {
+ logger?.LogOperationFailedDebug(nameof(Decode));
+ logger?.LogPolylineCannotBeShorterThanWarning(polylineSequence.Length, Defaults.Polyline.Block.Length.Min);
+
+ ExceptionGuard.ThrowInvalidPolylineLength(polylineSequence.Length, Defaults.Polyline.Block.Length.Min);
+ }
+ }
- throw new ArgumentException(string.Format(ExceptionMessageResource.PolylineCannotBeShorterThanExceptionMessage, sequence.Length), nameof(polyline));
+ ///
+ /// Validates the polyline format for allowed characters.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? logger) {
+ try {
+ PolylineEncoding.ValidateFormat(sequence.Span);
+ } catch (ArgumentException ex) {
+ logger?.LogInvalidPolylineFormatWarning(ex);
+
+ throw;
}
}
- }
- ///
- /// Converts the provided polyline instance into a for decoding.
- ///
- ///
- /// The instance containing the encoded polyline data to decode.
- ///
- ///
- /// A representing the encoded polyline data.
- ///
- protected abstract ReadOnlyMemory GetReadOnlyMemory(TPolyline polyline);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected abstract ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline);
- ///
- /// Creates a coordinate instance from the given latitude and longitude values.
- ///
- ///
- /// The latitude value.
- ///
- ///
- /// The longitude value.
- ///
- ///
- /// A coordinate instance of type .
- ///
- protected abstract TCoordinate CreateCoordinate(double latitude, double longitude);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected abstract TCoordinate CreateCoordinate(double latitude, double longitude);
+ }
}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs
index 6ea869bd..816684f3 100644
--- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs
+++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs
@@ -8,12 +8,12 @@ namespace PolylineAlgorithm.Abstraction;
using Microsoft.Extensions.Logging;
using PolylineAlgorithm;
using PolylineAlgorithm.Internal;
-using PolylineAlgorithm.Internal.Logging;
-using PolylineAlgorithm.Properties;
+using PolylineAlgorithm.Internal.Diagnostics;
+using PolylineAlgorithm.Internal.Diagnostics;
using System;
-using System.Collections;
-using System.Collections.Generic;
+using System.Buffers;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
///
/// Provides functionality to encode a collection of geographic coordinates into an encoded polyline string.
@@ -23,6 +23,7 @@ namespace PolylineAlgorithm.Abstraction;
/// This abstract class serves as a base for specific polyline encoders, allowing customization of the encoding process.
///
public abstract class AbstractPolylineEncoder : IPolylineEncoder {
+ private readonly ILogger> _logger;
///
/// Initializes a new instance of the class with default encoding options.
///
@@ -37,7 +38,14 @@ protected AbstractPolylineEncoder()
///
/// Thrown when is
protected AbstractPolylineEncoder(PolylineEncodingOptions options) {
- Options = options ?? throw new ArgumentNullException(nameof(options));
+ if (options is null) {
+ ExceptionGuard.ThrowArgumentNull(nameof(options));
+ }
+
+ Options = options;
+ _logger = Options
+ .LoggerFactory
+ .CreateLogger>();
}
///
@@ -53,7 +61,6 @@ protected AbstractPolylineEncoder(PolylineEncodingOptions options) {
///
///
/// An instance of representing the encoded coordinates.
- /// Returns if the input collection is empty or null.
///
///
/// Thrown when is .
@@ -61,129 +68,86 @@ protected AbstractPolylineEncoder(PolylineEncodingOptions options) {
///
/// Thrown when is an empty enumeration.
///
- public TPolyline Encode(IEnumerable coordinates) {
- var logger = Options
- .LoggerFactory
- .CreateLogger>();
-
- logger
- .LogOperationStartedInfo(nameof(Encode));
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains local methods. Actual method only 55 lines.")]
+ public TPolyline Encode(ReadOnlySpan coordinates) {
+ const string OperationName = nameof(Encode);
- Debug.Assert(coordinates is not null, "Coordinates cannot be null.");
+ _logger
+ .LogOperationStartedDebug(OperationName);
- ValidateNullCoordinates(coordinates, logger);
+ Debug.Assert(coordinates.Length >= 0, "Count must be non-negative.");
- int count = GetCount(coordinates);
+ ValidateEmptyCoordinates(ref coordinates, _logger);
- Debug.Assert(count >= 0, "Count must be non-negative.");
-
- ValidateEmptyCoordinates(logger, count);
-
- CoordinateVariance variance = new();
+ CoordinateDelta delta = new();
int position = 0;
int consumed = 0;
- int length = GetMaxBufferLength(count);
+ int length = GetMaxBufferLength(coordinates.Length);
- Span buffer = stackalloc char[length];
+ char[]? temp = length <= Options.StackAllocLimit
+ ? null
+ : ArrayPool.Shared.Rent(length);
- using var enumerator = coordinates.GetEnumerator();
+ Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length);
- while (enumerator.MoveNext()) {
- variance
- .Next(
- PolylineEncoding.Normalize(GetLatitude(enumerator.Current), CoordinateValueType.Latitude),
- PolylineEncoding.Normalize(GetLongitude(enumerator.Current), CoordinateValueType.Longitude)
- );
+ try {
+ for (var i = 0; i < coordinates.Length; i++) {
- ValidateBuffer(logger, variance, position, buffer);
+ delta
+ .Next(
+ PolylineEncoding.Normalize(GetLatitude(coordinates[i]), Options.Precision),
+ PolylineEncoding.Normalize(GetLongitude(coordinates[i]), Options.Precision)
+ );
- if (!PolylineEncoding.TryWriteValue(variance.Latitude, ref buffer, ref position)
- || !PolylineEncoding.TryWriteValue(variance.Longitude, ref buffer, ref position)
- ) {
- // This shouldn't happen, but if it does, log the error and throw an exception.
- logger
- .LogCannotWriteValueToBufferWarning(position, consumed);
- logger
- .LogOperationFailedInfo(nameof(Encode));
+ if (!PolylineEncoding.TryWriteValue(delta.Latitude, buffer, ref position)
+ || !PolylineEncoding.TryWriteValue(delta.Longitude, buffer, ref position)
+ ) {
+ // This shouldn't happen, but if it does, log the error and throw an exception.
+ _logger
+ .LogOperationFailedDebug(OperationName);
+ _logger
+ .LogCannotWriteValueToBufferWarning(position, consumed);
- throw new InvalidOperationException(ExceptionMessageResource.CouldNotWriteEncodedValueToTheBuffer);
- }
+ ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer();
- consumed++;
+ }
+
+ consumed++;
+ }
+ } finally {
+ if (temp is not null) {
+ ArrayPool.Shared.Return(temp);
+ }
}
- logger
- .LogOperationFinishedInfo(nameof(Encode));
+ _logger
+ .LogOperationFinishedDebug(OperationName);
return CreatePolyline(buffer[..position].ToString().AsMemory());
- static int GetCount(IEnumerable coordinates) => coordinates switch {
- ICollection collection => collection.Count,
- _ => coordinates.Count(),
- };
-
- static int GetRequiredLength(CoordinateVariance variance) =>
- PolylineEncoding.GetCharCount(variance.Latitude) + PolylineEncoding.GetCharCount(variance.Longitude);
-
- static int GetRemainingBufferSize(int position, int length) {
- Debug.Assert(length > 0, "Buffer length must be greater than zero.");
- Debug.Assert(position >= 0, "Position must be non-negative.");
- Debug.Assert(position < length, "Position must be less than buffer length.");
- Debug.Assert(length - position >= 0, "Remaining length must be non-negative.");
-
- return length - position;
- }
-
- int GetMaxBufferLength(int count) {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static int GetMaxBufferLength(int count) {
Debug.Assert(count > 0, "Count must be greater than zero.");
- int requestedBufferLength = count * Defaults.Polyline.Block.Length.Max;
+ int requestedBufferLength = count * 2 * Defaults.Polyline.Block.Length.Max;
- Debug.Assert(Options.MaxBufferLength > 0, "Max buffer length must be greater than zero.");
Debug.Assert(requestedBufferLength > 0, "Requested buffer length must be greater than zero.");
- if (requestedBufferLength > Options.MaxBufferLength) {
- logger
- .LogRequestedBufferSizeExceedsMaxBufferLengthWarning(requestedBufferLength, Options.MaxBufferLength);
-
- return Options.MaxBufferLength;
- }
-
return requestedBufferLength;
}
- static void ValidateNullCoordinates(IEnumerable coordinates, ILogger> logger) {
- if (coordinates is null) {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, ILogger logger) {
+ if (coordinates.Length < 1) {
logger
- .LogNullArgumentWarning(nameof(coordinates));
- logger
- .LogOperationFailedInfo(nameof(Encode));
-
- throw new ArgumentNullException(nameof(coordinates));
- }
- }
-
- static void ValidateEmptyCoordinates(ILogger> logger, int count) {
- if (count < 1) {
+ .LogOperationFailedDebug(OperationName);
logger
.LogEmptyArgumentWarning(nameof(coordinates));
- logger
- .LogOperationFailedInfo(nameof(Encode));
- throw new ArgumentException(ExceptionMessageResource.ArgumentCannotBeEmptyEnumerationMessage, nameof(coordinates));
- }
- }
-
- static void ValidateBuffer(ILogger> logger, CoordinateVariance variance, int position, Span buffer) {
- if (GetRemainingBufferSize(position, buffer.Length) < GetRequiredLength(variance)) {
- logger
- .LogInternalBufferOverflowWarning(position, buffer.Length, GetRequiredLength(variance));
- logger
- .LogOperationFailedInfo(nameof(Encode));
-
-
- throw new InternalBufferOverflowException();
+ ExceptionGuard.ThrwoArgumentCannotBeEmptyEnumerationMessage(nameof(coordinates));
}
}
}
@@ -195,7 +159,7 @@ static void ValidateBuffer(ILogger
/// An instance of representing the encoded polyline.
///
-
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected abstract TPolyline CreatePolyline(ReadOnlyMemory polyline);
///
@@ -205,7 +169,7 @@ static void ValidateBuffer(ILogger
/// The longitude value as a .
///
-
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected abstract double GetLongitude(TCoordinate current);
///
@@ -215,7 +179,7 @@ static void ValidateBuffer(ILogger
/// The latitude value as a .
///
-
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected abstract double GetLatitude(TCoordinate current);
}
diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs
index 363601ee..3937353c 100644
--- a/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs
+++ b/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs
@@ -10,7 +10,7 @@ namespace PolylineAlgorithm.Abstraction;
///
/// Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates.
///
-public interface IPolylineDecoder {
+public interface IPolylineDecoder {
///
/// Decodes the specified encoded polyline into a sequence of geographic coordinates.
///
diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs
index 8febc1bf..7fc93905 100644
--- a/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs
+++ b/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs
@@ -4,9 +4,6 @@
//
namespace PolylineAlgorithm.Abstraction;
-
-using System.Collections.Generic;
-
///
/// Defines a contract for encoding a sequence of geographic coordinates into an encoded polyline string.
///
@@ -20,5 +17,5 @@ public interface IPolylineEncoder {
///
/// A containing the encoded polyline string that represents the input coordinates.
///
- TPolyline Encode(IEnumerable coordinates);
+ TPolyline Encode(ReadOnlySpan coordinates);
}
diff --git a/src/PolylineAlgorithm/Coordinate.cs b/src/PolylineAlgorithm/Coordinate.cs
index 43156f29..62204e1f 100644
--- a/src/PolylineAlgorithm/Coordinate.cs
+++ b/src/PolylineAlgorithm/Coordinate.cs
@@ -5,10 +5,11 @@
namespace PolylineAlgorithm;
-using PolylineAlgorithm.Properties;
+using PolylineAlgorithm.Internal.Diagnostics;
using System;
using System.Diagnostics;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
///
@@ -18,13 +19,18 @@ namespace PolylineAlgorithm;
/// This struct is designed to be immutable and lightweight, providing a simple way to represent
/// geographic coordinates in degrees. It includes validation for latitude and longitude ranges
/// and provides methods for equality comparison and string representation.
+///
+/// Note: The value (0, 0) is a valid coordinate (Gulf of Guinea), but is also treated as the "default" value by .
///
[DebuggerDisplay("{ToString()}")]
-[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 16)]
+[StructLayout(LayoutKind.Auto)]
public readonly struct Coordinate : IEquatable {
///
/// Initializes a new instance of the struct with default values (0) for and .
///
+ ///
+ /// The default value (0, 0) is a valid coordinate (Gulf of Guinea), but is also treated as the "default" value by .
+ ///
public Coordinate() {
Latitude = default;
Longitude = default;
@@ -44,13 +50,7 @@ public Coordinate() {
/// or when is less than -180 or greater than 180.
///
public Coordinate(double latitude, double longitude) {
- if (latitude < -90 || latitude > 90 || double.IsNaN(latitude) || double.IsInfinity(latitude)) {
- throw new ArgumentOutOfRangeException(nameof(latitude), string.Format(ExceptionMessageResource.CoordinateValueMustBeBetweenValuesMessageFormat, "Latitude", -90, 90));
- }
-
- if (longitude < -180 || longitude > 180 || double.IsNaN(longitude) || double.IsInfinity(longitude)) {
- throw new ArgumentOutOfRangeException(nameof(longitude), string.Format(ExceptionMessageResource.CoordinateValueMustBeBetweenValuesMessageFormat, "Longitude", -180, 180));
- }
+ Validator.Validate(latitude, longitude);
Latitude = latitude;
Longitude = longitude;
@@ -69,18 +69,37 @@ public Coordinate(double latitude, double longitude) {
///
/// Determines whether this coordinate is the default value (both and are 0).
///
+ ///
+ /// The value (0, 0) is a valid coordinate (Gulf of Guinea), but is also treated as the "default" value by this method.
+ ///
///
/// if both latitude and longitude are 0; otherwise, .
///
public bool IsDefault()
- => Latitude == default
- && Longitude == default;
-
- #region Overrides
+ => Latitude.Equals(default)
+ && Longitude.Equals(default);
///
public override bool Equals(object? obj) => obj is Coordinate coordinate && Equals(coordinate);
+ ///
+ public bool Equals(Coordinate other) {
+ return Latitude.Equals(other.Latitude) &&
+ Longitude.Equals(other.Longitude);
+ }
+
+ ///
+ /// Determines whether two instances are approximately equal within a specified tolerance.
+ ///
+ /// The other coordinate to compare.
+ /// The maximum allowed difference for latitude and longitude.
+ ///
+ /// if both latitude and longitude differ by less than ; otherwise, .
+ ///
+ public bool Equals(Coordinate other, double tolerance)
+ => Math.Abs(Latitude - other.Latitude) < tolerance
+ && Math.Abs(Longitude - other.Longitude) < tolerance;
+
///
public override int GetHashCode() => HashCode.Combine(Latitude, Longitude);
@@ -94,20 +113,6 @@ public override string ToString() {
return $"{{ {nameof(Latitude)}: {Latitude.ToString("G", CultureInfo.InvariantCulture)}, {nameof(Longitude)}: {Longitude.ToString("G", CultureInfo.InvariantCulture)} }}";
}
- #endregion
-
- #region IEquatable implementation
-
- ///
- public bool Equals(Coordinate other) {
- return Latitude == other.Latitude &&
- Longitude == other.Longitude;
- }
-
- #endregion
-
- #region Equality operators
-
///
/// Determines whether two instances are equal.
///
@@ -128,5 +133,81 @@ public bool Equals(Coordinate other) {
///
public static bool operator !=(Coordinate left, Coordinate right) => !(left == right);
- #endregion
+ ///
+ /// Provides static methods for validating latitude and longitude values used in .
+ ///
+ ///
+ /// The Validator class ensures that latitude and longitude values are within their valid ranges:
+ ///
+ /// -
+ ///
Latitude must be between -90 and 90 degrees.
+ ///
+ /// -
+ ///
Longitude must be between -180 and 180 degrees.
+ ///
+ ///
+ /// If a value is out of range or not finite, an exception is thrown via .
+ ///
+ public static class Validator {
+ ///
+ /// Validates that the specified latitude is within the valid range of -90 to 90 degrees.
+ ///
+ /// The latitude value to validate.
+ ///
+ /// Thrown if is less than -90, greater than 90, or not a finite number.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ValidateLatitude(double latitude) {
+ const double min = -90;
+ const double max = 90;
+
+ ValidateValue(latitude, min, max, nameof(latitude));
+ }
+
+ ///
+ /// Validates that the specified longitude is within the valid range of -180 to 180 degrees.
+ ///
+ /// The longitude value to validate.
+ ///
+ /// Thrown if is less than -180, greater than 180, or not a finite number.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ValidateLongitude(double longitude) {
+ const double min = -180;
+ const double max = 180;
+
+ ValidateValue(longitude, min, max, nameof(longitude));
+ }
+
+ ///
+ /// Validates that the specified value is finite and within the specified range.
+ ///
+ /// The value to validate.
+ /// The minimum allowed value (inclusive).
+ /// The maximum allowed value (inclusive).
+ /// The name of the parameter being validated.
+ ///
+ /// Thrown if is not finite, less than , or greater than .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ValidateValue(double value, double min, double max, string paramName) {
+ if (!double.IsFinite(value) || value < min || value > max) {
+ ExceptionGuard.ThrowCoordinateValueOutOfRange(value, min, max, paramName);
+ }
+ }
+
+ ///
+ /// Validates both latitude and longitude values for a coordinate.
+ ///
+ /// The latitude value to validate.
+ /// The longitude value to validate.
+ ///
+ /// Thrown if either or is out of range or not finite.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void Validate(double latitude, double longitude) {
+ ValidateLatitude(latitude);
+ ValidateLongitude(longitude);
+ }
+ }
}
diff --git a/src/PolylineAlgorithm/CoordinateValueType.cs b/src/PolylineAlgorithm/CoordinateValueType.cs
deleted file mode 100644
index f576e8dd..00000000
--- a/src/PolylineAlgorithm/CoordinateValueType.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace PolylineAlgorithm;
-
-///
-/// Represents the type of a geographic coordinate value.
-///
-///
-/// This enumeration is used to specify whether a coordinate value represents latitude or
-/// longitude. Latitude values indicate the north-south position, while longitude values indicate the east-west
-/// position.
-///
-public enum CoordinateValueType : int {
- ///
- /// Represents no specific type. This value is used when the type is not applicable or not specified.
- ///
- None = 0,
- ///
- /// Represents a latitude value.
- ///
- Latitude = 1,
- ///
- /// Represents a longitude value.
- ///
- Longitude = 2
-}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/InvalidPolylineException.cs b/src/PolylineAlgorithm/Diagnostics/InvalidPolylineException.cs
similarity index 50%
rename from src/PolylineAlgorithm/InvalidPolylineException.cs
rename to src/PolylineAlgorithm/Diagnostics/InvalidPolylineException.cs
index 36250da6..79a84bfa 100644
--- a/src/PolylineAlgorithm/InvalidPolylineException.cs
+++ b/src/PolylineAlgorithm/Diagnostics/InvalidPolylineException.cs
@@ -3,12 +3,10 @@
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
-namespace PolylineAlgorithm;
+namespace PolylineAlgorithm.Diagnostics;
-using PolylineAlgorithm.Properties;
using System;
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
///
/// Exception thrown when a polyline is determined to be malformed or invalid during processing.
@@ -16,30 +14,32 @@ namespace PolylineAlgorithm;
///
/// This exception is used internally to indicate that a polyline string does not conform to the expected format or contains errors.
///
-[SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "Internal use only.")]
[DebuggerDisplay($"{nameof(InvalidPolylineException)}: {{ToString()}}")]
public sealed class InvalidPolylineException : Exception {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public InvalidPolylineException()
+ : base() { }
+
///
/// Initializes a new instance of the class with a specified error message.
///
///
/// The error message that describes the reason for the exception.
///
- private InvalidPolylineException(string message)
+ internal InvalidPolylineException(string message)
: base(message) { }
///
- /// Throws an with a message indicating the position in the polyline where the error was detected.
+ /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception.
///
- ///
- /// The zero-based index in the polyline string where the error occurred.
+ ///
+ /// The error message that explains the reason for the exception.
///
- ///
- /// Always thrown to indicate that the polyline is malformed at the specified position.
- ///
- internal static void Throw(long position) {
- Debug.Assert(position >= 0, "Position must be a non-negative value.");
-
- throw new InvalidPolylineException(string.Format(ExceptionMessageResource.PolylineStringIsMalformedMessage, position.ToString()));
- }
+ ///
+ /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
+ ///
+ public InvalidPolylineException(string message, Exception innerException)
+ : base(message, innerException) { }
}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs
index 403b94ea..2557e7a7 100644
--- a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs
+++ b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs
@@ -7,6 +7,7 @@ namespace PolylineAlgorithm.Extensions;
using PolylineAlgorithm;
using PolylineAlgorithm.Abstraction;
+using PolylineAlgorithm.Internal.Diagnostics;
using System;
using System.Collections.Generic;
@@ -31,7 +32,7 @@ public static class PolylineDecoderExtensions {
///
public static IEnumerable Decode(this IPolylineDecoder decoder, string polyline) {
if (decoder is null) {
- throw new ArgumentNullException(nameof(decoder));
+ ExceptionGuard.ThrowArgumentNull(nameof(decoder));
}
return decoder.Decode(Polyline.FromString(polyline));
@@ -54,7 +55,7 @@ public static IEnumerable Decode(this IPolylineDecoder
public static IEnumerable Decode(this IPolylineDecoder decoder, char[] polyline) {
if (decoder is null) {
- throw new ArgumentNullException(nameof(decoder));
+ ExceptionGuard.ThrowArgumentNull(nameof(decoder));
}
return decoder.Decode(Polyline.FromCharArray(polyline));
@@ -77,7 +78,7 @@ public static IEnumerable Decode(this IPolylineDecoder
public static IEnumerable Decode(this IPolylineDecoder decoder, ReadOnlyMemory polyline) {
if (decoder is null) {
- throw new ArgumentNullException(nameof(decoder));
+ ExceptionGuard.ThrowArgumentNull(nameof(decoder));
}
return decoder.Decode(Polyline.FromMemory(polyline));
diff --git a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs
index 14cecfad..113d3d09 100644
--- a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs
+++ b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs
@@ -1,62 +1,77 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
+
namespace PolylineAlgorithm.Extensions;
-using PolylineAlgorithm;
using PolylineAlgorithm.Abstraction;
+using PolylineAlgorithm.Internal.Diagnostics;
using System;
using System.Collections.Generic;
+#if NET5_0_OR_GREATER
+using System.Runtime.InteropServices;
+#endif
///
/// Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines.
///
public static class PolylineEncoderExtensions {
///
- /// Encodes a collection of instances into an encoded polyline.
+ /// Encodes a collection of instances into an encoded polyline.
///
///
/// The instance used to perform the encoding operation.
///
///
- /// The sequence of objects to encode.
+ /// The sequence of objects to encode.
///
///
- /// A representing the encoded polyline string for the provided coordinates.
+ /// A representing the encoded polyline string for the provided coordinates.
///
///
/// Thrown when is .
///
- public static Polyline Encode(this IPolylineEncoder encoder, ICollection coordinates) {
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "We need a list as we do need to marshal it as span.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0016:Prefer using collection abstraction instead of implementation", Justification = "We need a list as we do need to marshal it as span.")]
+ public static TPolyline Encode(this IPolylineEncoder encoder, List coordinates) {
if (encoder is null) {
- throw new ArgumentNullException(nameof(encoder));
+ ExceptionGuard.ThrowArgumentNull(nameof(encoder));
}
- return encoder.Encode(coordinates);
+ if (coordinates is null) {
+ ExceptionGuard.ThrowArgumentNull(nameof(coordinates));
+ }
+
+#if NET5_0_OR_GREATER
+ return encoder.Encode(CollectionsMarshal.AsSpan(coordinates));
+#else
+ return encoder.Encode([.. coordinates]);
+#endif
}
+
///
- /// Encodes an array of instances into an encoded polyline.
+ /// Encodes an array of instances into an encoded polyline.
///
///
/// The instance used to perform the encoding operation.
///
///
- /// The array of objects to encode.
+ /// The array of objects to encode.
///
///
- /// A representing the encoded polyline string for the provided coordinates.
+ /// A representing the encoded polyline string for the provided coordinates.
///
///
/// Thrown when is .
///
- public static Polyline Encode(this IPolylineEncoder encoder, Coordinate[] coordinates) {
+ public static TPolyline Encode(this IPolylineEncoder encoder, TCoordinate[] coordinates) {
if (encoder is null) {
- throw new ArgumentNullException(nameof(encoder));
+ ExceptionGuard.ThrowArgumentNull(nameof(encoder));
+ }
+
+ if (coordinates is null) {
+ ExceptionGuard.ThrowArgumentNull(nameof(coordinates));
}
- return encoder.Encode(coordinates);
+ return encoder.Encode(coordinates.AsSpan());
}
}
diff --git a/src/PolylineAlgorithm/Internal/CoordinateDelta.cs b/src/PolylineAlgorithm/Internal/CoordinateDelta.cs
new file mode 100644
index 00000000..830d90ef
--- /dev/null
+++ b/src/PolylineAlgorithm/Internal/CoordinateDelta.cs
@@ -0,0 +1,72 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Internal;
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+///
+/// Represents the difference (delta) in latitude and longitude between consecutive geographic coordinates.
+/// This struct is used to compute and store the change in coordinate values as integer deltas.
+///
+[DebuggerDisplay("{ToString(),nq}")]
+[StructLayout(LayoutKind.Auto)]
+internal struct CoordinateDelta {
+ private (int Latitude, int Longitude) _current;
+
+ ///
+ /// Initializes a new instance of the struct with the default latitude and longitude deltas.
+ ///
+ public CoordinateDelta() {
+ _current = (default, default);
+ }
+
+ ///
+ /// Gets the current delta in latitude between the most recent and previous coordinate.
+ ///
+ public int Latitude { get; private set; }
+
+ ///
+ /// Gets the current delta in longitude between the most recent and previous coordinate.
+ ///
+ public int Longitude { get; private set; }
+
+ ///
+ /// Updates the delta values based on the next latitude and longitude, and sets the current coordinate as next delta baseline.
+ ///
+ /// The next latitude value.
+ /// The next longitude value.
+
+ public void Next(int latitude, int longitude) {
+ Latitude = Delta(_current.Latitude, latitude);
+ Longitude = Delta(_current.Longitude, longitude);
+
+ _current.Latitude = latitude;
+ _current.Longitude = longitude;
+ }
+
+ ///
+ /// Calculates the delta between two coordinate values.
+ ///
+ ///
+ /// This method computes the difference between two integer coordinate values, handling cases where the values may be positive or negative.
+ ///
+ /// The previous coordinate value.
+ /// The next coordinate value.
+ /// The computed delta between and .
+
+ private static int Delta(int initial, int next) => next - initial;
+
+ ///
+ /// Returns a string representation of the current coordinate delta.
+ ///
+ ///
+ /// A string in the format { Coordinate: { Latitude: [int], Longitude: [int] }, Delta: { Latitude: [int], Longitude: [int] } } representing the current coordinate and deltas to previous coordinate.
+ ///
+ public override readonly string ToString() =>
+ $"{{ Coordinate: {{ Latitude: {_current.Latitude}, Longitude: {_current.Longitude} }}, " +
+ $"Delta: {{ Latitude: {Latitude}, Longitude: {Longitude} }} }}";
+}
diff --git a/src/PolylineAlgorithm/Internal/CoordinateVariance.cs b/src/PolylineAlgorithm/Internal/CoordinateVariance.cs
deleted file mode 100644
index e33f02af..00000000
--- a/src/PolylineAlgorithm/Internal/CoordinateVariance.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Internal;
-
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-///
-/// Represents the difference (variance) in latitude and longitude between consecutive geographic coordinates.
-/// This struct is used to compute and store the change in coordinate values as integer deltas.
-///
-[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")]
-[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 16)]
-internal struct CoordinateVariance {
- private (int Latitude, int Longitude) _current;
-
- ///
- /// Initializes a new instance of the struct with the default latitude and longitude deltas.
- ///
- public CoordinateVariance() {
- _current =(default, default);
- }
-
- ///
- /// Gets the current variance in latitude between the most recent and previous coordinate.
- ///
- public int Latitude { get; private set; }
-
- ///
- /// Gets the current variance in longitude between the most recent and previous coordinate.
- ///
- public int Longitude { get; private set; }
-
- ///
- /// Updates the variance values based on the next latitude and longitude, and sets the current coordinate as next variance baseline.
- ///
- /// The next latitude value.
- /// The next longitude value.
-
- public void Next(int latitude, int longitude) {
- Latitude = Variance(_current.Latitude, latitude);
- Longitude = Variance(_current.Longitude, longitude);
-
- _current.Latitude = latitude;
- _current.Longitude = longitude;
- }
-
- ///
- /// Calculates the variance (delta) between two coordinate values.
- ///
- ///
- /// This method computes the difference between two integer coordinate values, handling cases where the values may be positive or negative.
- ///
- /// The previous coordinate value.
- /// The next coordinate value.
- /// The computed variance between and .
-
- private static int Variance(int initial, int next) => (initial, next) switch {
- (0, 0) => 0,
- (0, _) => next,
- (_, 0) => -initial,
- ( < 0, < 0) => -(Math.Abs(next) - Math.Abs(initial)),
- ( < 0, > 0) => next + Math.Abs(initial),
- ( > 0, < 0) => -(Math.Abs(next) + initial),
- ( > 0, > 0) => next - initial,
- };
-
- ///
- /// Returns a string representation of the current coordinate variance.
- ///
- ///
- /// A string in the format { Coordinate: { Latitude: [int], Longitude: [int] }, Variance: { Latitude: [int], Longitude: [int] } } representing the current coordinate and deltas to previous coordinate.
- ///
- public override readonly string ToString()
- => $"{{ Coordinate: {{ Latitude: {Latitude}, Longitude: {Longitude} }}, Variance: {{ Latitude: {Latitude}, Longitude: {Longitude} }} }}";
-}
diff --git a/src/PolylineAlgorithm/Internal/Defaults.cs b/src/PolylineAlgorithm/Internal/Defaults.cs
index 3f62fac8..28f32b2a 100644
--- a/src/PolylineAlgorithm/Internal/Defaults.cs
+++ b/src/PolylineAlgorithm/Internal/Defaults.cs
@@ -26,10 +26,6 @@ internal static class Logging {
/// Contains default values and constants specific to the polyline encoding algorithm.
///
internal static class Algorithm {
- ///
- /// The precision factor used to round coordinate values during polyline encoding.
- ///
- internal const int Precision = 100_000;
///
/// The number of bits to shift during polyline encoding.
@@ -69,20 +65,6 @@ internal static class Latitude {
/// The maximum valid latitude value.
///
internal const double Max = 90.00000;
-
- ///
- /// Contains constants related to normalized latitude values.
- ///
- internal static class Normalized {
- ///
- /// The minimum normalized latitude value.
- ///
- internal const int Min = (int)(Latitude.Min * Algorithm.Precision);
- ///
- /// The maximum normalized latitude value.
- ///
- internal const int Max = (int)(Latitude.Max * Algorithm.Precision);
- }
}
///
@@ -101,20 +83,6 @@ internal static class Longitude {
/// The maximum valid longitude value.
///
internal const double Max = 180.00000;
-
- ///
- /// Contains constants related to normalized longitude values.
- ///
- internal static class Normalized {
- ///
- /// The minimum normalized latitude value.
- ///
- internal const int Min = (int)(Longitude.Min * Algorithm.Precision);
- ///
- /// The maximum normalized latitude value.
- ///
- internal const int Max = (int)(Longitude.Max * Algorithm.Precision);
- }
}
}
@@ -127,18 +95,18 @@ internal static class Polyline {
///
internal static class Block {
///
- /// Contains constants related to the length of encoded coordinates in polyline encoding.
+ /// Contains constants related to the length of encoded vakues in polyline encoding.
///
internal static class Length {
///
- /// The minimum number of characters required to represent an encoded coordinate.
+ /// The minimum number of characters required to represent an encoded value.
///
- internal const int Min = 2;
+ internal const int Min = 1;
///
- /// The maximum number of characters allowed to represent an encoded coordinate.
+ /// The maximum number of characters allowed to represent an encoded value.
///
- internal const int Max = 12;
+ internal const int Max = 7;
}
}
}
diff --git a/src/PolylineAlgorithm/Internal/Diagnostics/ExceptionGuard.cs b/src/PolylineAlgorithm/Internal/Diagnostics/ExceptionGuard.cs
new file mode 100644
index 00000000..d8c38362
--- /dev/null
+++ b/src/PolylineAlgorithm/Internal/Diagnostics/ExceptionGuard.cs
@@ -0,0 +1,213 @@
+namespace PolylineAlgorithm.Internal.Diagnostics;
+
+using PolylineAlgorithm.Diagnostics;
+using PolylineAlgorithm.Properties;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+
+#if NETSTANDARD2_1 || NET5_0
+using System.Runtime.CompilerServices;
+using static PolylineAlgorithm.Internal.Diagnostics.ExceptionGuard;
+#endif
+
+#if NET6_0_OR_GREATER
+using System.Diagnostics;
+#endif
+
+#if NET8_0_OR_GREATER
+using System.Text;
+#endif
+
+internal static class ExceptionGuard {
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowNotFiniteNumber(string paramName) {
+ throw new ArgumentOutOfRangeException(paramName, ExceptionMessage.GetArgumentValueMustBeFiniteNumber());
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowArgumentNull(string paramName) {
+ throw new ArgumentNullException(paramName);
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowBufferOverflow(string message) {
+ throw new OverflowException(message);
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowCoordinateValueOutOfRange(double value, double min, double max, string paramName) {
+ throw new ArgumentOutOfRangeException(paramName, ExceptionMessage.FormatCoordinateValueMustBeBetween(paramName, min, max));
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void StackAllocLimitMustBeEqualOrGreaterThan(int minValue, string paramName) {
+ throw new ArgumentOutOfRangeException(paramName, ExceptionMessage.FormatStackAllocLimitMustBeEqualOrGreaterThan(minValue));
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrwoArgumentCannotBeEmptyEnumerationMessage(string paramName) {
+ throw new ArgumentException(ExceptionMessage.GetArgumentCannotBeEmpty(), paramName);
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ internal static void ThrowCouldNotWriteEncodedValueToBuffer() {
+ throw new InvalidOperationException(ExceptionMessage.GetCouldNotWriteEncodedValueToTheBuffer());
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(int destinationLength, int polylineLength, string paramName) {
+
+ throw new ArgumentException(ExceptionMessage.FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(destinationLength, polylineLength), paramName);
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowInvalidPolylineLength(int length, int min) {
+ throw new InvalidPolylineException(ExceptionMessage.FormatInvalidPolylineLength(length, min));
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowInvalidPolylineCharacter(char character, int position) {
+ throw new InvalidPolylineException(ExceptionMessage.FormatInvalidPolylineCharacter(character, position));
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowPolylineBlockTooLong(int position) {
+ throw new InvalidPolylineException(ExceptionMessage.FormatPolylineBlockTooLong(position));
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowInvalidPolylineFormat(long position) {
+ throw new InvalidPolylineException(ExceptionMessage.FormatMalformedPolyline(position));
+ }
+
+#if NET6_0_OR_GREATER
+ [StackTraceHidden]
+#else
+ [MethodImpl(MethodImplOptions.NoInlining)]
+#endif
+ [DoesNotReturn]
+ internal static void ThrowInvalidPolylineBlockTerminator() {
+ throw new InvalidPolylineException(ExceptionMessage.GetInvalidPolylineBlockTerminator());
+ }
+
+ [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Internal use only.")]
+ internal static class ExceptionMessage {
+#if NET8_0_OR_GREATER
+ private static readonly CompositeFormat StackAllocLimitMustBeEqualOrGreaterThanFormat = CompositeFormat.Parse(ExceptionMessageResource.StackAllocLimitMustBeEqualOrGreaterThanFormat);
+ private static readonly CompositeFormat PolylineCannotBeShorterThanFormat = CompositeFormat.Parse(ExceptionMessageResource.PolylineCannotBeShorterThanFormat);
+ private static readonly CompositeFormat PolylineIsMalformedAtFormat = CompositeFormat.Parse(ExceptionMessageResource.PolylineIsMalformedAtFormat);
+ private static readonly CompositeFormat CoordinateValueMustBeBetweenFormat = CompositeFormat.Parse(ExceptionMessageResource.CoordinateValueMustBeBetweenValuesFormat);
+ private static readonly CompositeFormat PolylineBlockTooLongFormat = CompositeFormat.Parse(ExceptionMessageResource.PolylineBlockTooLongFormat);
+ private static readonly CompositeFormat InvalidPolylineCharacterFormat = CompositeFormat.Parse(ExceptionMessageResource.InvalidPolylineCharacterFormat);
+ private static readonly CompositeFormat DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat = CompositeFormat.Parse(ExceptionMessageResource.DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat);
+ private static readonly CompositeFormat InvalidPolylineLengthFormat = CompositeFormat.Parse(ExceptionMessageResource.InvalidPolylineLengthFormat);
+#else
+ private static readonly string StackAllocLimitMustBeEqualOrGreaterThanFormat = ExceptionMessageResource.StackAllocLimitMustBeEqualOrGreaterThanFormat;
+ private static readonly string PolylineCannotBeShorterThanFormat = ExceptionMessageResource.PolylineCannotBeShorterThanFormat;
+ private static readonly string PolylineIsMalformedAtFormat = ExceptionMessageResource.PolylineIsMalformedAtFormat;
+ private static readonly string CoordinateValueMustBeBetweenFormat = ExceptionMessageResource.CoordinateValueMustBeBetweenValuesFormat;
+ private static readonly string PolylineBlockTooLongFormat = ExceptionMessageResource.PolylineBlockTooLongFormat;
+ private static readonly string InvalidPolylineCharacterFormat = ExceptionMessageResource.InvalidPolylineCharacterFormat;
+ private static readonly string DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat = ExceptionMessageResource.DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat;
+ private static readonly string InvalidPolylineLengthFormat = ExceptionMessageResource.InvalidPolylineLengthFormat;
+#endif
+
+ private static readonly string ArgumentCannotBeEmptyMessage = ExceptionMessageResource.ArgumentCannotBeEmptyMessage;
+ private static readonly string CouldNotWriteEncodedValueToTheBufferMessage = ExceptionMessageResource.CouldNotWriteEncodedValueToTheBufferMessage;
+ private static readonly string InvalidPolylineBlockTerminatorMessage = ExceptionMessageResource.InvalidPolylineBlockTerminatorMessage;
+
+ internal static string FormatStackAllocLimitMustBeEqualOrGreaterThan(int minValue) =>
+ string.Format(CultureInfo.InvariantCulture, StackAllocLimitMustBeEqualOrGreaterThanFormat, minValue);
+
+ internal static string FormatPolylineCannotBeShorterThan(int length, int minLength) =>
+ string.Format(CultureInfo.InvariantCulture, PolylineCannotBeShorterThanFormat, length, minLength);
+
+ internal static string FormatMalformedPolyline(long position) =>
+ string.Format(CultureInfo.InvariantCulture, PolylineIsMalformedAtFormat, position);
+
+ internal static string FormatCoordinateValueMustBeBetween(string name, double min, double max) =>
+ string.Format(CultureInfo.InvariantCulture, CoordinateValueMustBeBetweenFormat, name, min, max);
+
+ internal static string FormatPolylineBlockTooLong(int position) =>
+ string.Format(CultureInfo.InvariantCulture, PolylineBlockTooLongFormat, position);
+
+ internal static string FormatInvalidPolylineCharacter(char character, int position) =>
+ string.Format(CultureInfo.InvariantCulture, InvalidPolylineCharacterFormat, character, position);
+
+ internal static string FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(int destinationLength, int polylineLength) =>
+ string.Format(CultureInfo.InvariantCulture, DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat, destinationLength, polylineLength);
+
+ internal static string FormatInvalidPolylineLength(int length, int min) =>
+ string.Format(CultureInfo.InvariantCulture, InvalidPolylineLengthFormat, length, min);
+
+ internal static string GetArgumentValueMustBeFiniteNumber() =>
+ ArgumentCannotBeEmptyMessage;
+
+ internal static string GetCouldNotWriteEncodedValueToTheBuffer() =>
+ CouldNotWriteEncodedValueToTheBufferMessage;
+
+ internal static string GetArgumentCannotBeEmpty() =>
+ ArgumentCannotBeEmptyMessage;
+ internal static string GetInvalidPolylineBlockTerminator() =>
+ InvalidPolylineBlockTerminatorMessage;
+ }
+}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/Internal/Diagnostics/LogDebugExtensions.cs b/src/PolylineAlgorithm/Internal/Diagnostics/LogDebugExtensions.cs
new file mode 100644
index 00000000..31b8db72
--- /dev/null
+++ b/src/PolylineAlgorithm/Internal/Diagnostics/LogDebugExtensions.cs
@@ -0,0 +1,25 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Internal.Diagnostics;
+
+using Microsoft.Extensions.Logging;
+
+internal static partial class LogDebugExtensions {
+ private const LogLevel LOG_LEVEL = LogLevel.Debug;
+ private const int EVENT_ID_BASE = (int)LOG_LEVEL * Defaults.Logging.LogLevelMultiplier;
+
+ [LoggerMessage(EVENT_ID_BASE + 1, LOG_LEVEL, "Operation {operationName} has started.")]
+ internal static partial void LogOperationStartedDebug(this ILogger logger, string operationName);
+
+ [LoggerMessage(EVENT_ID_BASE + 2, LOG_LEVEL, "Operation {operationName} has failed.")]
+ internal static partial void LogOperationFailedDebug(this ILogger logger, string operationName);
+
+ [LoggerMessage(EVENT_ID_BASE + 3, LOG_LEVEL, "Operation {operationName} has finished.")]
+ internal static partial void LogOperationFinishedDebug(this ILogger logger, string operationName);
+
+ [LoggerMessage(EVENT_ID_BASE + 4, LOG_LEVEL, "Decoded coordinate: (Latitude: {latitude}, Longitude: {longitude}) at position {position}.")]
+ internal static partial void LogDecodedCoordinateDebug(this ILogger logger, double latitude, double longitude, int position);
+}
diff --git a/src/PolylineAlgorithm/Internal/Logging/LogWarningExtensions.cs b/src/PolylineAlgorithm/Internal/Diagnostics/LogWarningExtensions.cs
similarity index 81%
rename from src/PolylineAlgorithm/Internal/Logging/LogWarningExtensions.cs
rename to src/PolylineAlgorithm/Internal/Diagnostics/LogWarningExtensions.cs
index 4a58057f..28d7c933 100644
--- a/src/PolylineAlgorithm/Internal/Logging/LogWarningExtensions.cs
+++ b/src/PolylineAlgorithm/Internal/Diagnostics/LogWarningExtensions.cs
@@ -3,7 +3,7 @@
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
-namespace PolylineAlgorithm.Internal.Logging;
+namespace PolylineAlgorithm.Internal.Diagnostics;
using Microsoft.Extensions.Logging;
@@ -23,12 +23,15 @@ internal static partial class LogWarningExtensions {
[LoggerMessage(EVENT_ID_BASE + 4, LOG_LEVEL, "Cannot write to internal buffer at position {position}. Current coordinate is at index {coordinateIndex}.")]
internal static partial void LogCannotWriteValueToBufferWarning(this ILogger logger, int position, int coordinateIndex);
- [LoggerMessage(EVENT_ID_BASE + 5, LOG_LEVEL, "Argument {argumentName} is too short. Minimal length is {minimumLength}. Actual length is {actualLength}.")]
- internal static partial void LogPolylineCannotBeShorterThanWarning(this ILogger logger, string argumentName, int actualLength, int minimumLength);
+ [LoggerMessage(EVENT_ID_BASE + 5, LOG_LEVEL, "Polyline is too short. Minimal length is {minimumLength}. Actual length is {actualLength}.")]
+ internal static partial void LogPolylineCannotBeShorterThanWarning(this ILogger logger, int actualLength, int minimumLength);
[LoggerMessage(EVENT_ID_BASE + 6, LOG_LEVEL, "Requested buffer size of {requestedBufferLength} exceeds maximum allowed buffer length of {maxBufferLength}.")]
internal static partial void LogRequestedBufferSizeExceedsMaxBufferLengthWarning(this ILogger logger, int requestedBufferLength, int maxBufferLength);
[LoggerMessage(EVENT_ID_BASE + 7, LOG_LEVEL, "Polyline is invalid or malformed at position {position}.")]
internal static partial void LogInvalidPolylineWarning(this ILogger logger, int position);
+
+ [LoggerMessage(EVENT_ID_BASE + 8, LOG_LEVEL, "Polyline is invalid or malformed.")]
+ internal static partial void LogInvalidPolylineFormatWarning(this ILogger logger, Exception ex);
}
diff --git a/src/PolylineAlgorithm/Internal/Logging/LogInfoExtensions.cs b/src/PolylineAlgorithm/Internal/Logging/LogInfoExtensions.cs
deleted file mode 100644
index b3360a1b..00000000
--- a/src/PolylineAlgorithm/Internal/Logging/LogInfoExtensions.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Internal.Logging;
-
-using Microsoft.Extensions.Logging;
-
-internal static partial class LogInfoExtensions {
- private const LogLevel LOG_LEVEL = LogLevel.Information;
- private const int EVENT_ID_BASE = (int)LOG_LEVEL * Defaults.Logging.LogLevelMultiplier;
-
- [LoggerMessage(EVENT_ID_BASE + 1, LOG_LEVEL, "Operation {operationName} has started.")]
- internal static partial void LogOperationStartedInfo(this ILogger logger, string operationName);
-
- [LoggerMessage(EVENT_ID_BASE + 2, LOG_LEVEL, "Operation {operationName} has failed.")]
- internal static partial void LogOperationFailedInfo(this ILogger logger, string operationName);
-
- [LoggerMessage(EVENT_ID_BASE + 3, LOG_LEVEL, "Operation {operationName} has finished.")]
- internal static partial void LogOperationFinishedInfo(this ILogger logger, string operationName);
-}
diff --git a/src/PolylineAlgorithm/Internal/Pow10.cs b/src/PolylineAlgorithm/Internal/Pow10.cs
new file mode 100644
index 00000000..55834bec
--- /dev/null
+++ b/src/PolylineAlgorithm/Internal/Pow10.cs
@@ -0,0 +1,40 @@
+namespace PolylineAlgorithm.Internal;
+
+using System;
+
+///
+/// Provides optimized calculation of powers of 10 for precision-based operations.
+///
+///
+/// This class caches common powers of 10 (10^0 through 10^9) for efficient lookup,
+/// falling back to for larger exponents.
+///
+internal static class Pow10 {
+ ///
+ /// Pre-computed powers of 10 from 10^0 to 10^9.
+ ///
+ private static readonly uint[] _pow10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+
+ ///
+ /// Returns the power of 10 for the specified precision level.
+ ///
+ ///
+ /// The exponent for the base 10 (i.e., the number of decimal places).
+ ///
+ ///
+ /// The value of 10 raised to the power of as a .
+ ///
+ ///
+ /// If is between 0 and 9 (inclusive), the method returns a cached value
+ /// for optimal performance. For other values, the result is computed using .
+ /// The calculation is performed in a checked context to ensure that arithmetic overflow is detected.
+ ///
+ ///
+ /// Thrown if the computed value exceeds .
+ ///
+ public static uint GetFactor(uint precision) {
+ checked {
+ return precision < _pow10.Length ? _pow10[precision] : (uint)Math.Pow(10, precision);
+ }
+ }
+}
diff --git a/src/PolylineAlgorithm/Polyline.cs b/src/PolylineAlgorithm/Polyline.cs
index d5e51295..9c29145d 100644
--- a/src/PolylineAlgorithm/Polyline.cs
+++ b/src/PolylineAlgorithm/Polyline.cs
@@ -5,9 +5,10 @@
namespace PolylineAlgorithm;
-using PolylineAlgorithm.Properties;
+using PolylineAlgorithm.Internal.Diagnostics;
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
///
@@ -20,13 +21,11 @@ namespace PolylineAlgorithm;
[StructLayout(LayoutKind.Auto)]
[DebuggerDisplay("Value: {ToDebugString()}, IsEmpty: {IsEmpty}, Length: {Length}")]
public readonly struct Polyline : IEquatable {
- private readonly ReadOnlyMemory _value;
-
///
/// Initializes a new, empty instance of the struct.
///
public Polyline() {
- _value = ReadOnlyMemory.Empty;
+ Value = ReadOnlyMemory.Empty;
}
///
@@ -36,13 +35,13 @@ public Polyline() {
/// A read-only memory region of characters representing an encoded polyline.
///
private Polyline(ReadOnlyMemory value) {
- _value = value;
+ Value = value;
}
///
/// Gets the underlying read-only sequence of characters representing the polyline.
///
- internal readonly ReadOnlyMemory Value => _value;
+ internal readonly ReadOnlyMemory Value { get; }
///
/// Gets a value indicating whether this is empty.
@@ -50,9 +49,9 @@ private Polyline(ReadOnlyMemory value) {
public readonly bool IsEmpty => Value.IsEmpty;
///
- /// Gets the length of the polyline in characters.
+ /// Gets the length of the polyline in characters as an .
///
- public readonly long Length => Value.Length;
+ public readonly int Length => Value.Length;
///
/// Copies the characters of this polyline to the specified destination array.
@@ -68,14 +67,14 @@ private Polyline(ReadOnlyMemory value) {
///
public void CopyTo(char[] destination) {
if (destination is null) {
- throw new ArgumentNullException(nameof(destination));
+ ExceptionGuard.ThrowArgumentNull(nameof(destination));
}
if (destination.Length < Length) {
- throw new ArgumentException(ExceptionMessageResource.DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthMessage, nameof(destination));
+ ExceptionGuard.ThrowDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(destination.Length, Length, nameof(destination));
}
- _value.CopyTo(destination);
+ Value.CopyTo(destination);
}
///
@@ -89,7 +88,7 @@ public override string ToString() {
return string.Empty;
}
- return Value.ToString();
+ return new string(Value.Span);
}
///
@@ -98,15 +97,29 @@ public override string ToString() {
///
/// A string that includes the polyline value, truncated if necessary, for debugging purposes.
///
+ [ExcludeFromCodeCoverage(
+#if NET5_0_OR_GREATER
+ Justification = "Only used for debugging."
+#endif
+ )]
private string ToDebugString() {
if (IsEmpty) {
return string.Empty;
}
- return Value.Length <= 32 ? $"\"{Value}\"" : $"\"{Value[..10]}...{Value[^10..]}\"";
+ var span = Value.Span;
+ if (span.Length <= 32)
+ return $"\"{new string(span)}\"";
+ return $"\"{new string(span.Slice(0, 10))}...{new string(span.Slice(span.Length - 10))}\"";
}
- ///
+ ///
+ /// Determines whether the current instance is equal to another instance.
+ ///
+ /// The other to compare with.
+ ///
+ /// if the instances are equal; otherwise, .
+ ///
public bool Equals(Polyline other) {
if ((IsEmpty != other.IsEmpty) || (Length != other.Length)) {
return false;
@@ -116,13 +129,22 @@ public bool Equals(Polyline other) {
}
///
- public override bool Equals(object obj) {
+ public override bool Equals(object? obj) {
return obj is Polyline other && Equals(other);
}
///
public override int GetHashCode() {
- return Value.GetHashCode();
+ // Content-based hash code for value equality
+ var span = Value.Span;
+ if (span.IsEmpty)
+ return 0;
+ unchecked {
+ int hash = 17;
+ for (int i = 0; i < span.Length; i++)
+ hash = hash * 31 + span[i];
+ return hash;
+ }
}
///
@@ -151,8 +173,6 @@ public override int GetHashCode() {
return !(left == right);
}
- #region Factory methods
-
///
/// Creates a from a Unicode character array.
///
@@ -167,7 +187,7 @@ public override int GetHashCode() {
///
public static Polyline FromCharArray(char[] polyline) {
if (polyline is null) {
- throw new ArgumentNullException(nameof(polyline));
+ ExceptionGuard.ThrowArgumentNull(nameof(polyline));
}
return FromMemory(polyline.AsMemory());
@@ -187,7 +207,7 @@ public static Polyline FromCharArray(char[] polyline) {
///
public static Polyline FromString(string polyline) {
if (polyline is null) {
- throw new ArgumentNullException(nameof(polyline));
+ ExceptionGuard.ThrowArgumentNull(nameof(polyline));
}
return FromMemory(polyline.AsMemory());
@@ -209,6 +229,4 @@ public static Polyline FromMemory(ReadOnlyMemory polyline) {
return new Polyline(polyline);
}
-
- #endregion
}
diff --git a/src/PolylineAlgorithm/PolylineAlgorithm.csproj b/src/PolylineAlgorithm/PolylineAlgorithm.csproj
index 54b8cc74..599857af 100644
--- a/src/PolylineAlgorithm/PolylineAlgorithm.csproj
+++ b/src/PolylineAlgorithm/PolylineAlgorithm.csproj
@@ -1,19 +1,7 @@
- netstandard2.1
- 13.0
- enable
- enable
- true
- en
-
-
-
- All
- latest
- true
- true
+ netstandard2.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;
@@ -22,13 +10,13 @@
true
-
+
pdbonly
true
snupkg
-
+
True
@@ -44,7 +32,7 @@
-
+
True
\
@@ -55,7 +43,8 @@
all
runtime; build; native; contentfiles; analyzers
-
+
+
@@ -68,11 +57,11 @@
-
- ExceptionMessageResource.resx
- True
- True
-
+
+ True
+ True
+ ExceptionMessageResource.resx
+
diff --git a/src/PolylineAlgorithm/PolylineDecoder.cs b/src/PolylineAlgorithm/PolylineDecoder.cs
index 2e398d28..9543b842 100644
--- a/src/PolylineAlgorithm/PolylineDecoder.cs
+++ b/src/PolylineAlgorithm/PolylineDecoder.cs
@@ -23,7 +23,7 @@ protected override Coordinate CreateCoordinate(double latitude, double longitude
}
///
- protected override ReadOnlyMemory GetReadOnlyMemory(Polyline polyline) {
+ protected override ReadOnlyMemory GetReadOnlyMemory(in Polyline polyline) {
return polyline.Value;
}
}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/PolylineEncoding.cs b/src/PolylineAlgorithm/PolylineEncoding.cs
index 75100f5e..045445da 100644
--- a/src/PolylineAlgorithm/PolylineEncoding.cs
+++ b/src/PolylineAlgorithm/PolylineEncoding.cs
@@ -4,9 +4,13 @@
//
namespace PolylineAlgorithm;
+
using PolylineAlgorithm.Internal;
-using PolylineAlgorithm.Properties;
+using PolylineAlgorithm.Internal.Diagnostics;
+
using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
///
/// Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing
@@ -18,28 +22,152 @@ namespace PolylineAlgorithm;
/// longitude.
public static class PolylineEncoding {
///
- /// Attempts to read a value from the specified buffer and updates the variance.
+ /// Normalizes a geographic coordinate value to an integer representation based on the specified precision.
+ ///
+ ///
+ ///
+ /// This method converts a floating-point coordinate value into a normalized integer by multiplying it by 10 raised
+ /// to the power of the specified precision, then rounding the result using the specified strategy.
+ ///
+ ///
+ /// For example, with the default precision of 5:
+ ///
+ /// A value of 37.78903 becomes 3778903
+ /// A value of -122.4123 becomes -12241230
+ ///
+ ///
+ ///
+ /// The method validates that the input value is finite (not NaN or infinity) before performing normalization.
+ /// If the precision is 0, the value is rounded without multiplication.
+ ///
+ ///
+ ///
+ /// The numeric value to normalize. Must be a finite number (not NaN or infinity).
+ ///
+ ///
+ /// The number of decimal places of precision to preserve in the normalized value.
+ /// The value is multiplied by 10^ before rounding.
+ /// Default is 5, which is standard for polyline encoding.
+ ///
+ ///
+ /// An integer representing the normalized value. Returns 0 if the input is 0.0 .
+ ///
+ ///
+ /// Thrown when is not a finite number (NaN or infinity).
+ ///
+ ///
+ /// Thrown when the normalized result exceeds the range of a 32-bit signed integer during the conversion from double to int.
+ ///
+ public static int Normalize(double value, uint precision = 5) {
+ // Fast return if the value is zero, return 0 as the normalized value.
+ if (value.Equals(default)) {
+ return 0;
+ }
+
+ // Validate that the value is finite and not NaN or Infinity.
+ if (!double.IsFinite(value)) {
+ ExceptionGuard.ThrowNotFiniteNumber(nameof(value));
+ }
+
+ // Fast return if precision is zero, return current value converted to Int32.
+ if (precision == default) {
+ return (int)Math.Truncate(value);
+ }
+
+ uint factor = Pow10.GetFactor(precision);
+
+ checked {
+ return (int)(Math.Truncate(value * 10 * factor) / 10);
+ }
+
+ }
+
+ ///
+ /// Converts a normalized integer coordinate value back to its floating-point representation based on the specified precision.
+ ///
+ ///
+ ///
+ /// This method reverses the normalization performed by . It takes an integer value and converts it
+ /// to a double by dividing it by 10 raised to the power of the specified precision. If is 0,
+ /// the value is returned as a double without division.
+ ///
+ ///
+ /// The calculation is performed inside a block to ensure that any arithmetic overflow is detected
+ /// and an is thrown.
+ ///
+ ///
+ /// For example, with a precision of 5:
+ ///
+ /// A value of 3778903 becomes 37.78903
+ /// A value of -12241230 becomes -122.4123
+ ///
+ ///
+ ///
+ /// If the input is 0 , the method returns 0.0 immediately.
+ ///
+ ///
+ ///
+ /// The integer value to denormalize. Typically produced by the method.
+ ///
+ ///
+ /// The number of decimal places used during normalization. Default is 5, matching standard polyline encoding precision.
+ ///
+ ///
+ /// The denormalized floating-point coordinate value.
+ ///
+ ///
+ /// Thrown if the arithmetic operation overflows during conversion.
+ ///
+ public static double Denormalize(int value, uint precision = 5) {
+ if (value.Equals(default)) {
+ return default;
+ }
+
+ // Fast return if precision is zero, return current value converted to Int32.
+ if (precision == default) {
+ return value;
+ }
+
+ uint factor = Pow10.GetFactor(precision);
+
+ checked {
+
+ return value / (double)factor;
+ }
+ }
+
+ ///
+ /// Attempts to read an encoded integer value from a polyline buffer, updating the specified delta and position.
///
- /// This method processes the buffer starting at the specified position and attempts to decode a value.
- /// The decoded value is used to update the parameter. The method stops reading when a
- /// termination condition is met or the end of the buffer is reached.
- ///
- /// A reference to the integer that will be updated based on the value read from the buffer.
+ ///
+ ///
+ /// This method decodes a value from a polyline-encoded character buffer, starting at the given position. It reads
+ /// characters sequentially, applying the polyline decoding algorithm, and updates the with
+ /// the decoded value. The position is advanced as characters are processed.
+ ///
+ ///
+ /// The decoding process continues until a character with a value less than the algorithm's space constant is encountered,
+ /// which signals the end of the encoded value. If the buffer is exhausted before a complete value is read, the method returns .
+ ///
+ ///
+ /// The decoded value is added to using zigzag decoding, which handles both positive and negative values.
+ ///
+ ///
+ ///
+ /// Reference to the integer accumulator that will be updated with the decoded value.
///
///
- /// A reference to the read-only memory buffer containing the data to be processed.
+ /// The buffer containing polyline-encoded characters.
///
///
- /// A reference to the current position within the buffer. The position is incremented as the method reads data.
+ /// Reference to the current position in the buffer. This value is updated as characters are read.
///
///
- /// if a value was successfully read and the end of the buffer was not reached; otherwise, .
+ /// if a value was successfully read and decoded; if the buffer ended before a complete value was read.
///
-
- public static bool TryReadValue(ref int variance, ref ReadOnlyMemory buffer, ref int position) {
+ public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref int position) {
// Validate that the position is within the bounds of the buffer.
- if (position == buffer.Length) {
+ if (position >= buffer.Length) {
return false;
}
@@ -61,91 +189,69 @@ public static bool TryReadValue(ref int variance, ref ReadOnlyMemory buffe
}
}
- variance += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1;
+ delta += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1;
// If the end of the buffer was reached without reading a complete value, return false.
return chunk < Defaults.Algorithm.Space;
}
- ///
- /// Converts a normalized integer value to its denormalized double representation based on the specified type.
- ///
- /// The denormalization process divides the input value by a predefined precision factor to
- /// produce the resulting double. Ensure that is validated against the specified before calling this method.
- ///
- /// The normalized integer value to be denormalized. Must be within the valid range for the specified .
- ///
- ///
- /// The type that defines the valid range for .
- ///
- ///
- /// The denormalized double representation of the input value. Returns if is .
- ///
- ///
- /// Thrown when is outside the valid range for the specified .
- ///
-
- public static double Denormalize(int value, CoordinateValueType type) {
- // Validate that the type is not None, as it does not represent a valid coordinate value type.
- if (type == CoordinateValueType.None) {
- throw new ArgumentOutOfRangeException(nameof(type), string.Format(ExceptionMessageResource.ArgumentCannotBeCoordinateValueTypeMessageFormat, type.ToString()));
- }
-
- // Validate that the value is finite and within the acceptable range for the specified type.
- if (!ValidateValue(value, type)) {
- throw new ArgumentOutOfRangeException(nameof(value), value, string.Format(ExceptionMessageResource.ArgumentOutOfRangeForSpecifiedCoordinateValueTypeMessageFormat, type.ToString().ToLowerInvariant()));
- }
-
- // Return fast if the value is zero, return 0.0 as the denormalized value.
- if (value == 0) {
- return 0.0;
- }
-
- return Math.Truncate((double)value) / Defaults.Algorithm.Precision;
- }
///
- /// Attempts to write a value derived from the specified into the provided at the given .
+ /// Attempts to write an encoded integer value to a polyline buffer, updating the specified position.
///
///
- /// This method performs bounds checking to ensure that the buffer has sufficient space to
- /// accommodate the calculated value. If the buffer does not have enough space, the method returns without modifying the buffer or position.
+ ///
+ /// This method encodes an integer delta value into a polyline-encoded format and writes it to the provided character buffer,
+ /// starting at the given position. It applies zigzag encoding followed by the polyline encoding algorithm to represent
+ /// both positive and negative values efficiently.
+ ///
+ ///
+ /// The encoding process first converts the value using zigzag encoding (left shift by 1, with bitwise inversion for negative values),
+ /// then writes it as a sequence of characters. Each character encodes 5 bits of data, with continuation bits indicating whether
+ /// more characters follow. The position is advanced as characters are written.
+ ///
+ ///
+ /// Before writing, the method validates that sufficient space is available in the buffer by calling .
+ /// If the buffer does not have enough remaining capacity, the method returns without modifying the buffer or position.
+ ///
+ ///
+ /// This method is the inverse of and can be used to encode coordinate deltas for polyline serialization.
+ ///
///
- ///
- /// The integer value used to calculate the output to be written into the buffer.
+ ///
+ /// The integer value to encode and write to the buffer. This value typically represents the difference between consecutive
+ /// coordinate values in polyline encoding.
///
///
- /// A reference to the span of characters where the value will be written.
+ /// The destination buffer where the encoded characters will be written. Must have sufficient capacity to hold the encoded value.
///
///
- /// A reference to the current position in the buffer where writing begins. This value is updated to reflect the new
- /// position after writing.
+ /// Reference to the current position in the buffer. This value is updated as characters are written to reflect the new position
+ /// after encoding is complete.
///
///
- /// if the value was successfully written to the buffer; otherwise, .
+ /// if the value was successfully encoded and written to the buffer; if the buffer
+ /// does not have sufficient remaining capacity to hold the encoded value.
///
-
- public static bool TryWriteValue(int variance, ref Span buffer, ref int position) {
+ public static bool TryWriteValue(int delta, Span buffer, ref int position) {
// Validate that the position and required space for write is within the bounds of the buffer.
- if (buffer.Length < position + GetCharCount(variance)) {
+ if (buffer[position..].Length < GetRequiredBufferSize(delta)) {
return false;
}
- int rem = variance << 1;
+ int rem = delta << 1;
- // If the variance is negative, we need to invert the bits to get the correct representation.
- if (variance < 0) {
+ // If the delta is negative, we need to invert the bits to get the correct representation.
+ if (delta < 0) {
rem = ~rem;
}
// Write the value to the buffer in a way that encodes it using the specified algorithm.
while (rem >= Defaults.Algorithm.Space) {
- buffer[position++] = (char)((Defaults.Algorithm.Space | rem & Defaults.Algorithm.UnitSeparator) + Defaults.Algorithm.QuestionMark);
+ buffer[position++] =
+ (char)((Defaults.Algorithm.Space
+ | (rem & Defaults.Algorithm.UnitSeparator))
+ + Defaults.Algorithm.QuestionMark);
rem >>= Defaults.Algorithm.ShiftLength;
}
@@ -156,97 +262,200 @@ public static bool TryWriteValue(int variance, ref Span buffer, ref int po
}
///
- /// Normalizes a given numeric value based on the specified type and precision settings.
+ /// Calculates the number of characters required to encode a delta value in polyline format.
///
///
- /// This method validates the input value to ensure it is finite and within the acceptable range
- /// for the specified type. If the value is valid, it applies a normalization algorithm using a predefined precision
- /// factor.
+ ///
+ /// This method determines how many characters will be needed to represent an integer delta value when encoded
+ /// using the polyline encoding algorithm. It performs the same zigzag encoding transformation as
+ /// but only calculates the required buffer size without actually writing any data.
+ ///
+ ///
+ /// The calculation process:
+ ///
+ /// Applies zigzag encoding: left-shifts the value by 1 bit, then inverts all bits if the original value was negative
+ /// Counts how many 5-bit chunks are needed to represent the encoded value
+ /// Each chunk requires one character, with a minimum of 1 character for any value
+ ///
+ ///
+ ///
+ /// This method is useful for pre-allocating buffers of the correct size before encoding polyline data, helping to avoid
+ /// buffer overflow checks during the actual encoding process.
+ ///
+ ///
+ /// The method uses a internally to prevent overflow during the left-shift operation on large negative values.
+ ///
///
- ///
- /// The numeric value to normalize. Must be a finite number.
- ///
- ///
- /// The type against which the value is validated. Determines the acceptable range for the value.
+ ///
+ /// The integer delta value to calculate the encoded size for. This value typically represents the difference between
+ /// consecutive coordinate values in polyline encoding.
///
///
- /// An integer representing the normalized value. Returns 0 if the input value is 0.0 .
+ /// The number of characters required to encode the specified delta value. The minimum return value is 1.
///
- ///
- /// Thrown when is not a finite number or is outside the valid range for the specified
- /// .
- ///
+ ///
+ public static int GetRequiredBufferSize(int delta) {
+ long rem = (long)delta << 1;
- public static int Normalize(double value, CoordinateValueType type) {
- // Validate that the type is not None, as it does not represent a valid coordinate value type.
- if (type == CoordinateValueType.None) {
- throw new ArgumentOutOfRangeException(nameof(type), string.Format(ExceptionMessageResource.ArgumentCannotBeCoordinateValueTypeMessageFormat, type.ToString()));
+ if (delta < 0) {
+ rem = ~rem;
}
- // Validate that the value is finite and not NaN or Infinity.
- if (double.IsNaN(value) || double.IsInfinity(value)) {
- throw new ArgumentOutOfRangeException(nameof(value), ExceptionMessageResource.ArgumentValueMustBeFiniteNumber);
- }
+ int size = 1;
- // Validate that the value is within the acceptable range for the specified type.
- if (!ValidateValue(value, type)) {
- throw new ArgumentOutOfRangeException(nameof(value), value, string.Format(ExceptionMessageResource.ArgumentOutOfRangeForSpecifiedCoordinateValueTypeMessageFormat, type.ToString().ToLowerInvariant()));
+ while (rem >= Defaults.Algorithm.Space) {
+ rem >>= Defaults.Algorithm.ShiftLength;
+ size++;
}
- // Fast return if the value is zero, return 0 as the normalized value.
- if (value == 0.0) {
- return 0;
- }
+ return size;
+ }
+
+ #region Validation
+
+ ///
+ /// The minimum valid character value for polyline encoding, corresponding to the ASCII value of '?' (63).
+ ///
+ private const ushort Min = Defaults.Algorithm.QuestionMark;
+
+ ///
+ /// The maximum valid character value for polyline encoding, calculated as the sum of two question mark values ('?' + '?', or 63 + 63 = 126).
+ ///
+ private const ushort Max = (Defaults.Algorithm.QuestionMark + Defaults.Algorithm.QuestionMark);
+
+ ///
+ /// The character value that marks the end of a polyline block, calculated as the sum of the question mark value and the space value ('?' + ' ', or 63 + 32 = 95).
+ ///
+ private const ushort End = (Defaults.Algorithm.QuestionMark + Defaults.Algorithm.Space);
+
+ ///
+ /// SIMD vector containing the minimum valid character value for efficient range validation of polyline segments.
+ ///
+ private static readonly Vector MinVector = new(Min);
+
+ ///
+ /// SIMD vector containing the maximum valid character value for efficient range validation of polyline segments.
+ ///
+ private static readonly Vector MaxVector = new(Max);
- return (int)Math.Round(value * Defaults.Algorithm.Precision);
+ ///
+ /// Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct.
+ ///
+ ///
+ ///
+ /// This method performs two levels of validation on the provided polyline segment:
+ ///
+ ///
+ /// -
+ ///
+ /// Character Range Validation: Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive).
+ /// Uses SIMD acceleration for efficient validation of large segments.
+ ///
+ ///
+ /// -
+ ///
+ /// Block Structure Validation: Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator.
+ ///
+ ///
+ ///
+ ///
+ /// If an invalid character or block structure is detected, an is thrown with details about the error.
+ ///
+ ///
+ /// A span representing the polyline segment to validate.
+ ///
+ /// Thrown when an invalid character is found or the block structure is invalid.
+ ///
+ public static void ValidateFormat(ReadOnlySpan polyline) {
+ // 1. SIMD character check (reuse existing method)
+ ValidateCharRange(polyline);
+ // 2. Block structure check
+ ValidateBlockLength(polyline);
}
///
- /// Determines the number of characters required to represent the specified integer value within predefined
- /// variance ranges.
+ /// Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding.
///
///
- /// The method uses predefined ranges to efficiently determine the character count. Smaller
- /// values require fewer characters, while larger values require more. This method is optimized for performance
- /// using a switch expression.
+ ///
+ /// Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected.
+ ///
+ ///
+ /// The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an is thrown.
+ ///
///
- ///
- /// The integer value for which the character count is calculated. Must be within the range of a 32-bit signed
- /// integer.
- ///
- ///
- /// The number of characters required to represent the value, based on its magnitude.
- /// Returns a value between 1 and 6 inclusive.
- ///
+ /// A span representing the polyline segment to validate.
+ ///
+ /// Thrown when an invalid character is found in the polyline segment.
+ ///
+ public static void ValidateCharRange(ReadOnlySpan polyline) {
+ int length = polyline.Length;
+ int vectorSize = Vector.Count;
+
+ int i = 0;
+ for (; i <= length - vectorSize; i += vectorSize) {
+ var span = MemoryMarshal.Cast(polyline.Slice(i, vectorSize));
+#if NET5_0_OR_GREATER
+ var chars = new Vector(span);
+#else
+ var chars = new Vector(span.ToArray());
+#endif
+ var belowMin = Vector.LessThan(chars, MinVector);
+ var aboveMax = Vector.GreaterThan(chars, MaxVector);
+ if (Vector.BitwiseOr(belowMin, aboveMax) != Vector.Zero) {
+ // Fallback to scalar check for this block
+ for (int j = 0; j < vectorSize; j++) {
+ char character = polyline[i + j];
+ if (character < Min || character > Max) {
+ ExceptionGuard.ThrowInvalidPolylineCharacter(character, i + j);
+ }
+ }
+ }
+ }
- public static int GetCharCount(int variance) => variance switch {
- // DO NOT CHANGE THE ORDER. We are skipping inside exclusive ranges as those are covered by previous statements.
- >= -16 and <= +15 => 1,
- >= -512 and <= +511 => 2,
- >= -16384 and <= +16383 => 3,
- >= -524288 and <= +524287 => 4,
- >= -16777216 and <= +16777215 => 5,
- _ => 6,
- };
+ for (; i < length; i++) {
+ char character = polyline[i];
+ if (character < Min || character > Max) {
+ ExceptionGuard.ThrowInvalidPolylineCharacter(character, i);
+ }
+ }
+ }
///
- /// Validates whether the specified denormalized value falls within the acceptable range for the given value type.
+ /// Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly.
///
- ///
- /// The denormalized value to validate.
- ///
- ///
- /// The type of value to validate, such as latitude or longitude.
- ///
- ///
- /// if the is within the valid range for the specified ; otherwise, .
- ///
- private static bool ValidateValue(T value, CoordinateValueType type) => (type, value) switch {
- (CoordinateValueType.Latitude, int normalized) when normalized >= Defaults.Coordinate.Latitude.Normalized.Min && normalized <= Defaults.Coordinate.Latitude.Normalized.Max => true,
- (CoordinateValueType.Longitude, int normalized) when normalized >= Defaults.Coordinate.Longitude.Normalized.Min && normalized <= Defaults.Coordinate.Longitude.Normalized.Max => true,
- (CoordinateValueType.Latitude, double denormalized) when denormalized >= Defaults.Coordinate.Latitude.Min && denormalized <= Defaults.Coordinate.Latitude.Max => true,
- (CoordinateValueType.Longitude, double denormalized) when denormalized >= Defaults.Coordinate.Longitude.Min && denormalized <= Defaults.Coordinate.Longitude.Max => true,
- _ => false,
- };
-}
+ ///
+ ///
+ /// Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value).
+ /// Throws an if any block exceeds 7 characters or if the polyline does not end with a valid block terminator.
+ ///
+ ///
+ /// A span representing the polyline segment to validate.
+ ///
+ /// Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator.
+ ///
+ public static void ValidateBlockLength(ReadOnlySpan polyline) {
+ int blockLen = 0;
+ bool foundBlockEnd = false;
+
+ for (int i = 0; i < polyline.Length; i++) {
+ blockLen++;
+
+ if (polyline[i] < End) {
+ foundBlockEnd = true;
+ if (blockLen > Defaults.Polyline.Block.Length.Max) {
+ ExceptionGuard.ThrowPolylineBlockTooLong(i - blockLen + 1);
+ }
+ blockLen = 0;
+ } else {
+ foundBlockEnd = false;
+ }
+ }
+
+ if (!foundBlockEnd) {
+ ExceptionGuard.ThrowInvalidPolylineBlockTerminator();
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/PolylineEncodingOptions.cs b/src/PolylineAlgorithm/PolylineEncodingOptions.cs
index ae8d1a6f..33bd8cb6 100644
--- a/src/PolylineAlgorithm/PolylineEncodingOptions.cs
+++ b/src/PolylineAlgorithm/PolylineEncodingOptions.cs
@@ -8,37 +8,76 @@ namespace PolylineAlgorithm;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
///
-/// Options for configuring polyline encoding.
+/// Provides configuration options for polyline encoding operations.
///
///
-/// This class allows you to set options such as buffer size and logger factory for encoding operations.
+///
+/// This class allows you to configure various aspects of polyline encoding, including:
+///
+///
+/// The level for coordinate encoding
+/// The for memory allocation strategy
+/// The for diagnostic logging
+///
+///
+/// All properties have internal setters and should be configured through a builder or factory pattern.
+///
///
-[DebuggerDisplay("MaxBufferSize: {MaxBufferSize}, MaxBufferLength: {MaxBufferLength}, LoggerFactoryType: {LoggerFactory.GetType().Name}")]
+[DebuggerDisplay("StackAllocLimit: {StackAllocLimit}, Precision: {Precision}, LoggerFactoryType: {GetLoggerFactoryType()}")]
public sealed class PolylineEncodingOptions {
///
- /// Gets the maximum buffer size for encoding operations.
+ /// Gets the logger factory used for diagnostic logging during encoding operations.
///
+ ///
+ /// An instance. Defaults to .
+ ///
///
- /// The default maximum buffer size is 64,000 bytes (64 KB). This can be adjusted based on the expected size of the polyline data, but should be enough for common cases.
+ /// The default logger factory is , which does not log any messages.
+ /// To enable logging, provide a custom implementation.
///
- public int MaxBufferSize { get; internal set; } = 64_000;
+ public ILoggerFactory LoggerFactory { get; internal set; } = NullLoggerFactory.Instance;
///
- /// Gets or sets the precision for encoding coordinates.
+ /// Gets the precision level used for encoding coordinate values.
///
+ ///
+ /// The number of decimal places to use when encoding coordinate values. Defaults to 5.
+ ///
///
- /// The default logger factory is , which does not log any messages.
+ ///
+ /// The precision determines the number of decimal places to which each coordinate value (latitude or longitude)
+ /// is multiplied and truncated (not rounded) before encoding. For example, a precision of 5 means each coordinate is multiplied by 10^5
+ /// and truncated to an integer before encoding.
+ ///
+ ///
+ /// This setting does not directly correspond to a physical distance or accuracy in meters, but rather controls
+ /// the granularity of the encoded values.
+ ///
///
- public ILoggerFactory LoggerFactory { get; internal set; } = NullLoggerFactory.Instance;
-
+ public uint Precision { get; internal set; } = 5;
///
- /// Gets the maximum length of the encoded polyline string.
+ /// Gets the maximum buffer size (in characters) that can be allocated on the stack for encoding operations.
///
+ ///
+ /// The maximum number of characters for stack allocation using stackalloc char[] . Defaults to 512.
+ ///
///
- /// The maximum length is calculated based on the buffer size divided by the size of a character.
+ /// When the required buffer size for encoding exceeds this limit, memory will be allocated on the heap instead of the stack.
+ /// This setting specifically applies to stack allocation of character arrays (stackalloc char[] ) used during polyline encoding,
+ /// balancing performance and stack safety.
///
- internal int MaxBufferLength => MaxBufferSize / sizeof(char);
+ public int StackAllocLimit { get; internal set; } = 512;
+
+ ///
+ /// Returns the type name of the logger factory for debugging purposes.
+ ///
+ ///
+ /// A string containing the type name of the current instance.
+ ///
+ [ExcludeFromCodeCoverage]
+ private string GetLoggerFactoryType() => LoggerFactory.GetType().Name;
}
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs b/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs
index a555b11f..24fa7572 100644
--- a/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs
+++ b/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs
@@ -7,13 +7,14 @@ namespace PolylineAlgorithm;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-using PolylineAlgorithm.Properties;
+using PolylineAlgorithm.Internal.Diagnostics;
///
/// Provides a builder for configuring options for polyline encoding operations.
///
-public class PolylineEncodingOptionsBuilder {
- private int _bufferSize = 64_000;
+public sealed class PolylineEncodingOptionsBuilder {
+ private uint _precision = 5;
+ private int _stackAllocLimit = 512;
private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance;
private PolylineEncodingOptionsBuilder() { }
@@ -36,41 +37,65 @@ public static PolylineEncodingOptionsBuilder Create() {
///
public PolylineEncodingOptions Build() {
return new PolylineEncodingOptions {
- MaxBufferSize = _bufferSize,
- LoggerFactory = _loggerFactory
+ Precision = _precision,
+ StackAllocLimit = _stackAllocLimit,
+ LoggerFactory = _loggerFactory,
};
}
///
- /// Sets the buffer size for encoding operations.
+ /// Configures the buffer size used for stack allocation during polyline encoding operations.
///
- ///
- /// The maximum buffer size. Must be greater than 11.
+ ///
+ /// The maximum buffer size to use for stack allocation. Must be greater than or equal to 1.
+ ///
+ ///
+ /// Returns the current instance for method chaining.
+ ///
+ ///
+ /// Thrown if is less than 1.
+ ///
+ ///
+ /// This method allows customization of the internal buffer size for encoding, which can impact performance and memory usage.
+ ///
+ public PolylineEncodingOptionsBuilder WithStackAllocLimit(int stackAllocLimit) {
+ const int minStackAllocLimit = 1;
+
+ if (minStackAllocLimit > stackAllocLimit) {
+ ExceptionGuard.StackAllocLimitMustBeEqualOrGreaterThan(minStackAllocLimit, nameof(stackAllocLimit));
+ }
+
+ _stackAllocLimit = stackAllocLimit;
+
+ return this;
+ }
+
+ ///
+ /// Sets the precision for encoding values.
+ ///
+ ///
+ /// The number of decimal places to use for encoding values. Default is 5.
///
///
/// The current builder instance.
///
- /// Thrown when is less than or equal to 11.
- public PolylineEncodingOptionsBuilder WithMaxBufferSize(int bufferSize) {
- _bufferSize = bufferSize > 11 ? bufferSize : throw new ArgumentOutOfRangeException(nameof(bufferSize), string.Format(ExceptionMessageResource.BufferSizeMustBeGreaterThanMessageFormat, 11));
+ public PolylineEncodingOptionsBuilder WithPrecision(uint precision) {
+ _precision = precision;
return this;
}
///
- /// Sets the logger factory for logging during encoding operations.
+ /// Configures the to be used for logging during polyline encoding operations.
///
///
- /// The instance of a logger factory.
+ /// The instance to use for logging. If , a will be used instead.
///
///
- /// The current builder instance.
+ /// Returns the current instance for method chaining.
///
- ///
- /// Thrown when is .
- ///
public PolylineEncodingOptionsBuilder WithLoggerFactory(ILoggerFactory loggerFactory) {
- _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
+ _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
return this;
}
diff --git a/src/PolylineAlgorithm/Properties/ExceptionMessageResource.Designer.cs b/src/PolylineAlgorithm/Properties/ExceptionMessageResource.Designer.cs
index 04d5a153..e913e00c 100644
--- a/src/PolylineAlgorithm/Properties/ExceptionMessageResource.Designer.cs
+++ b/src/PolylineAlgorithm/Properties/ExceptionMessageResource.Designer.cs
@@ -19,7 +19,7 @@ namespace PolylineAlgorithm.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class ExceptionMessageResource {
@@ -61,92 +61,119 @@ internal ExceptionMessageResource() {
}
///
- /// Looks up a localized string similar to Argument cannot be CoordinateValueType.{0}..
+ /// Looks up a localized string similar to Argument cannot be an empty..
///
- internal static string ArgumentCannotBeCoordinateValueTypeMessageFormat {
+ internal static string ArgumentCannotBeEmptyMessage {
get {
- return ResourceManager.GetString("ArgumentCannotBeCoordinateValueTypeMessageFormat", resourceCulture);
+ return ResourceManager.GetString("ArgumentCannotBeEmptyMessage", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Argument cannot be an empty enumeration..
+ /// Looks up a localized string similar to Value {0} is out of range. Expected range between {1} and {2}..
///
- internal static string ArgumentCannotBeEmptyEnumerationMessage {
+ internal static string ArgumentOutOfRangeFormat {
get {
- return ResourceManager.GetString("ArgumentCannotBeEmptyEnumerationMessage", resourceCulture);
+ return ResourceManager.GetString("ArgumentOutOfRangeFormat", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Value is out of range for the specified type {0}..
+ /// Looks up a localized string similar to Value must be a finite number..
///
- internal static string ArgumentOutOfRangeForSpecifiedCoordinateValueTypeMessageFormat {
+ internal static string ArgumentValueMustBeFiniteNumber {
get {
- return ResourceManager.GetString("ArgumentOutOfRangeForSpecifiedCoordinateValueTypeMessageFormat", resourceCulture);
+ return ResourceManager.GetString("ArgumentValueMustBeFiniteNumber", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Value must be a finite number..
+ /// Looks up a localized string similar to {0} must be between {1} and {2}..
///
- internal static string ArgumentValueMustBeFiniteNumber {
+ internal static string CoordinateValueMustBeBetweenValuesFormat {
get {
- return ResourceManager.GetString("ArgumentValueMustBeFiniteNumber", resourceCulture);
+ return ResourceManager.GetString("CoordinateValueMustBeBetweenValuesFormat", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Buffer size must be greater than {0}..
+ /// Looks up a localized string similar to Encoded values couldn't be written to the buffer..
///
- internal static string BufferSizeMustBeGreaterThanMessageFormat {
+ internal static string CouldNotWriteEncodedValueToTheBufferMessage {
get {
- return ResourceManager.GetString("BufferSizeMustBeGreaterThanMessageFormat", resourceCulture);
+ return ResourceManager.GetString("CouldNotWriteEncodedValueToTheBufferMessage", resourceCulture);
}
}
///
- /// Looks up a localized string similar to {0} must be between {1} and {2}..
+ /// Looks up a localized string similar to Destination array length {0} must be greater than the polyline length {1}..
+ ///
+ internal static string DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat {
+ get {
+ return ResourceManager.GetString("DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Polyline does not end with a valid block terminator..
+ ///
+ internal static string InvalidPolylineBlockTerminatorMessage {
+ get {
+ return ResourceManager.GetString("InvalidPolylineBlockTerminatorMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Polyline contains invalid character '{0}' at position {1}..
///
- internal static string CoordinateValueMustBeBetweenValuesMessageFormat {
+ internal static string InvalidPolylineCharacterFormat {
get {
- return ResourceManager.GetString("CoordinateValueMustBeBetweenValuesMessageFormat", resourceCulture);
+ return ResourceManager.GetString("InvalidPolylineCharacterFormat", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Encoded values couldn't be written to the buffer. Please, report a bug on GitHub with reproducible sample. Thank you, Pete..
+ /// Looks up a localized string similar to Polyline must be at least {1} characters long, but was {0}..
///
- internal static string CouldNotWriteEncodedValueToTheBuffer {
+ internal static string InvalidPolylineLengthFormat {
get {
- return ResourceManager.GetString("CouldNotWriteEncodedValueToTheBuffer", resourceCulture);
+ return ResourceManager.GetString("InvalidPolylineLengthFormat", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Destination array length must be greater than the polyline length..
+ /// Looks up a localized string similar to Block at position {0} exceeds 7 characters..
///
- internal static string DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthMessage {
+ internal static string PolylineBlockTooLongFormat {
get {
- return ResourceManager.GetString("DestinationArrayLengthMustBeEqualOrGreaterThanPolylineLengthMessage", resourceCulture);
+ return ResourceManager.GetString("PolylineBlockTooLongFormat", resourceCulture);
}
}
///
- /// Looks up a localized string similar to Argument cannot be shorten than 2 characters. Actual length: {0}..
+ /// Looks up a localized string similar to Argument cannot be shorten than {1} characters. Value: {0}..
///
- internal static string PolylineCannotBeShorterThanExceptionMessage {
+ internal static string PolylineCannotBeShorterThanFormat {
get {
- return ResourceManager.GetString("PolylineCannotBeShorterThanExceptionMessage", resourceCulture);
+ return ResourceManager.GetString("PolylineCannotBeShorterThanFormat", resourceCulture);
}
}
///
/// Looks up a localized string similar to Polyline is malformed at position {0}..
///
- internal static string PolylineStringIsMalformedMessage {
+ internal static string PolylineIsMalformedAtFormat {
+ get {
+ return ResourceManager.GetString("PolylineIsMalformedAtFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Stack alloc limit must be equal or greater than {0}..
+ ///
+ internal static string StackAllocLimitMustBeEqualOrGreaterThanFormat {
get {
- return ResourceManager.GetString("PolylineStringIsMalformedMessage", resourceCulture);
+ return ResourceManager.GetString("StackAllocLimitMustBeEqualOrGreaterThanFormat", resourceCulture);
}
}
}
diff --git a/src/PolylineAlgorithm/Properties/ExceptionMessageResource.resx b/src/PolylineAlgorithm/Properties/ExceptionMessageResource.resx
index bf47eeee..d5108c8d 100644
--- a/src/PolylineAlgorithm/Properties/ExceptionMessageResource.resx
+++ b/src/PolylineAlgorithm/Properties/ExceptionMessageResource.resx
@@ -117,34 +117,43 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- Argument cannot be an empty enumeration.
+
+ Argument cannot be an empty.
-
+
Polyline is malformed at position {0}.
-
- Argument cannot be shorten than 2 characters. Actual length: {0}.
+
+ Argument cannot be shorten than {1} characters. Value: {0}.
Value must be a finite number.
-
- Value is out of range for the specified type {0}.
+
+ Value {0} is out of range. Expected range between {1} and {2}.
-
- Argument cannot be CoordinateValueType.{0}.
+
+ Stack alloc limit must be equal or greater than {0}.
-
- Buffer size must be greater than {0}.
+
+ Destination array length {0} must be greater than the polyline length {1}.
-
- Destination array length must be greater than the polyline length.
-
-
+
{0} must be between {1} and {2}.
-
- Encoded values couldn't be written to the buffer. Please, report a bug on GitHub with reproducible sample. Thank you, Pete.
+
+ Encoded values couldn't be written to the buffer.
+
+
+ Block at position {0} exceeds 7 characters.
+
+
+ Polyline contains invalid character '{0}' at position {1}.
+
+
+ Polyline must be at least {1} characters long, but was {0}.
+
+
+ Polyline does not end with a valid block terminator.
\ No newline at end of file
diff --git a/src/PolylineAlgorithm/Properties/GlobalSuppressions.cs b/src/PolylineAlgorithm/Properties/GlobalSuppressions.cs
new file mode 100644
index 00000000..48f8c35a
--- /dev/null
+++ b/src/PolylineAlgorithm/Properties/GlobalSuppressions.cs
@@ -0,0 +1 @@
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "We do target multiple frameworks. Not all have this method available.")]
diff --git a/src/PolylineAlgorithm/README.md b/src/PolylineAlgorithm/README.md
new file mode 100644
index 00000000..16794e2a
--- /dev/null
+++ b/src/PolylineAlgorithm/README.md
@@ -0,0 +1,79 @@
+# PolylineAlgorithm for .NET
+
+A modern, fully compliant Google Encoded Polyline Algorithm library for .NET Standard 2.1+, supporting strong input validation, extensibility for custom coordinate types, and robust performance.
+
+## Features
+
+- Google-compliant polyline encoding/decoding for geographic coordinates
+- Immutable, strongly-typed data structures: `Coordinate`, `Polyline`
+- Predefined encoder/decoder types for easy usage
+- Extensible APIs for custom coordinate and polyline types
+- Robust input validation and descriptive exceptions
+- Configurable with `PolylineEncodingOptions` (buffer, logging, etc.)
+- Thread-safe, stateless APIs
+- Benchmarks and unit tests for correctness and performance
+- Auto-generated API docs ([API Reference](https://petesramek.github.io/polyline-algorithm-csharp/))
+- Supports .NET Core, .NET 5+, Xamarin, Unity, Blazor via `netstandard2.1`
+
+## Installation
+
+```shell
+dotnet add package PolylineAlgorithm
+```
+
+or via NuGet PMC:
+
+```powershell
+Install-Package PolylineAlgorithm
+```
+
+## Quick Start
+
+### Encode coordinates
+
+```csharp
+using PolylineAlgorithm;
+
+var coordinates = new List
+{
+ new Coordinate(48.858370, 2.294481),
+ new Coordinate(51.500729, -0.124625)
+};
+
+var encoder = new PolylineEncoder();
+Polyline encoded = encoder.Encode(coordinates);
+
+Console.WriteLine(encoded.ToString()); // Print encoded polyline string
+```
+
+### Decode polyline
+
+```csharp
+using PolylineAlgorithm;
+
+var decoder = new PolylineDecoder();
+Polyline polyline = Polyline.FromString("yseiHoc_MwacOjnwM");
+IEnumerable decoded = decoder.Decode(polyline);
+```
+
+## Advanced Usage
+
+- Custom coordinate/polyline types are supported via `AbstractPolylineEncoder` and `AbstractPolylineDecoder`.
+- Additional configuration via `PolylineEncodingOptionsBuilder`.
+
+> See [API Reference](https://petesramek.github.io/polyline-algorithm-csharp/) for full documentation.
+
+## FAQ
+
+- **What coordinate ranges are valid?**
+ Latitude: -90..90, Longitude: -180..180 (throws `ArgumentOutOfRangeException` for invalid input)
+- **What .NET versions are supported?**
+ Any environment supporting `netstandard2.1`
+- **How do I customize encoder options?**
+ Use `PolylineEncodingOptionsBuilder` and pass to the encoder constructor.
+- **Where can I get help?**
+ [GitHub issues](https://github.com/petesramek/polyline-algorithm-csharp/issues)
+
+## License
+
+MIT License © Pete Sramek
diff --git a/tests/PolylineAlgorithm.Tests/AbstractPolylineDecoderTest.cs b/tests/PolylineAlgorithm.Tests/AbstractPolylineDecoderTest.cs
deleted file mode 100644
index 07c738d2..00000000
--- a/tests/PolylineAlgorithm.Tests/AbstractPolylineDecoderTest.cs
+++ /dev/null
@@ -1,179 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using PolylineAlgorithm;
-using PolylineAlgorithm.Abstraction;
-using PolylineAlgorithm.Utility;
-using System;
-
-[TestClass]
-public class AbstractPolylineDecoderTest {
- private static readonly PolylineDecoder _decoder = new();
-
- public static IEnumerable CoordinateCount => [[1], [10], [100], [1_000]];
-
- public static IEnumerable<(double, double)> NotANumberAndInfinityCoordinates => StaticValueProvider.Invalid.GetNotANumberAndInfinityCoordinates();
-
- public static IEnumerable<(double, double)> MinAndMaxCoordinates => StaticValueProvider.Invalid.GetMinAndMaxCoordinates();
-
- public static IEnumerable InvalidPolylines => StaticValueProvider.Invalid.GetInvalidPolylines().Select(p => [p]);
-
- [TestMethod]
- public void Constructor_Parameterless_Ok() {
- // Arrange && Act
- var decoder = new PolylineDecoder();
-
- // Assert
- Assert.IsNotNull(decoder);
- Assert.IsNotNull(decoder.Options);
- }
-
- [TestMethod]
- public void Constructor_Options_Instance_Ok() {
- // Arrange
- var options = new PolylineEncodingOptions();
-
- // Act
- var decoder = new PolylineDecoder(options);
-
- // Assert
- Assert.IsNotNull(decoder);
- Assert.AreSame(options, decoder.Options);
- }
-
- [TestMethod]
- public void Constructor_Null_Options_Throws_ArgumentNullException() {
- // Arrange
- static void New() => new PolylineDecoder(null!);
-
- // Act
- var exception = Assert.ThrowsExactly(New);
-
- // Assert
- Assert.AreEqual("options", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- public void Decode_NullPolyline_Throws_ArgumentException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => _decoder.Decode(null!).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.AreEqual("polyline", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- public void Decode_EmptyPolyline_Throws_ArgumentException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => _decoder.Decode(string.Empty).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.AreEqual("polyline", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- public void Decode_WhitespacePolyline_Throws_ArgumentException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => _decoder.Decode(" ").ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.AreEqual("polyline", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- [DynamicData(nameof(InvalidPolylines), DynamicDataSourceType.Property)]
- public void Decode_InvalidPolyline_Throws_InvalidPolylineException(string polyline) {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- void Decode() => _decoder.Decode(polyline).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
-
- [TestMethod]
- public void Decode_ShortPolyline_Throws_InvalidPolylineException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => _decoder.Decode("?").ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount))]
- public void Encode_RandomValue_ValidInput_Ok(int count) {
- // Arrange
- string polyline = RandomValueProvider.GetPolyline(count);
- IEnumerable<(double Latitude, double Longitude)> expected = RandomValueProvider.GetCoordinates(count);
-
- // Act
- var result = _decoder.Decode(polyline);
-
- // Assert
- CollectionAssert.AreEqual(expected.ToArray(), result.ToArray());
- }
-
- [TestMethod]
- public void Decode_StaticValue_ValidInput_Ok() {
- // Arrange
- string polyline = StaticValueProvider.Valid.GetPolyline();
- IEnumerable<(double Latitude, double Longitude)> expected = StaticValueProvider.Valid.GetCoordinates();
-
- // Act
- var result = _decoder.Decode(polyline);
-
- // Assert
- CollectionAssert.AreEqual(expected.ToArray(), result.ToArray());
- }
-
- public class PolylineDecoder : AbstractPolylineDecoder {
- public PolylineDecoder()
- : base() { }
-
- public PolylineDecoder(PolylineEncodingOptions options)
- : base(options) { }
-
- protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) {
- return (latitude, longitude);
- }
-
- protected override ReadOnlyMemory GetReadOnlyMemory(string? polyline) {
- return polyline.AsMemory();
- }
- }
-}
diff --git a/tests/PolylineAlgorithm.Tests/AbstractPolylineEncoderTest.cs b/tests/PolylineAlgorithm.Tests/AbstractPolylineEncoderTest.cs
deleted file mode 100644
index daf05237..00000000
--- a/tests/PolylineAlgorithm.Tests/AbstractPolylineEncoderTest.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using PolylineAlgorithm;
-using PolylineAlgorithm.Abstraction;
-using PolylineAlgorithm.Utility;
-using System;
-
-[TestClass]
-public class AbstractPolylineEncoderTest {
- private static readonly PolylineEncoder _encoder = new();
-
- public static IEnumerable CoordinateCount => [[1], [10], [100], [1_000]];
-
- public static IEnumerable<(double, double)> NotANumberAndInfinityCoordinates => StaticValueProvider.Invalid.GetNotANumberAndInfinityCoordinates();
-
- public static IEnumerable<(double, double)> MinAndMaxCoordinates => StaticValueProvider.Invalid.GetMinAndMaxCoordinates();
-
-
- [TestMethod]
- public void Constructor_Parameterless_Ok() {
- // Arrange && Act
- var encoder = new PolylineEncoder();
-
- // Assert
- Assert.IsNotNull(encoder);
- Assert.IsNotNull(encoder.Options);
- }
-
- [TestMethod]
- public void Constructor_ValidOptions_Ok() {
- // Arrange
- var options = new PolylineEncodingOptions();
-
- // Act
- var encoder = new PolylineEncoder(options);
-
- // Assert
- Assert.IsNotNull(encoder);
- Assert.AreSame(options, encoder.Options);
- }
-
-
- [TestMethod]
- public void Constructor_Null_Options_Throws_ArgumentNullException() {
- // Arrange
- static void New() => new PolylineEncoder(null!);
-
- // Act
- var exception = Assert.ThrowsExactly(New);
-
- // Assert
- Assert.AreEqual("options", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- public void Encode_NullCoordinates_Throws_ArgumentException() {
- // Arrange
- static void Encode() => _encoder.Encode(null!);
-
- // Act
- var exception = Assert.ThrowsExactly(Encode);
-
- // Assert
- Assert.AreEqual("coordinates", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- public void Encode_EmptyCoordinates_Throws_ArgumentException() {
- // Arrange
- static void Encode() => _encoder.Encode([]);
-
- // Act
- var exception = Assert.ThrowsExactly(Encode);
-
- // Assert
- Assert.AreEqual("coordinates", exception.ParamName);
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- public void Encode_BufferTooSmall_Throws_InternalBufferOverflowException() {
- // Arrange
- PolylineEncoder _encoder = new(new PolylineEncodingOptions { MaxBufferSize = 12 });
- IEnumerable<(double Latitude, double Longitude)> coordinates = RandomValueProvider.GetCoordinates(2);
-
- // Act
- var exception = Assert.ThrowsExactly(() => _encoder.Encode(coordinates));
-
- // Assert
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- [DynamicData(nameof(NotANumberAndInfinityCoordinates))]
- public void Encode_NotANumberAndInfinityCoordinate_Throws_ArgumentOutOfRangeException((double, double) coordinate) {
- // Arrange
-
- // Act
- var exception = Assert.ThrowsExactly(() => _encoder.Encode([coordinate]));
-
- // Assert
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- [DynamicData(nameof(MinAndMaxCoordinates))]
- public void Encode_MinAndMaxCoordinate_Throws_ArgumentOutOfRangeException((double, double) coordinate) {
- // Arrange
-
- // Act
- var exception = Assert.ThrowsExactly(() => _encoder.Encode([coordinate]));
-
- // Assert
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount))]
- public void Encode_RandomValue_ValidInput_Ok(int count) {
- // Arrange
- IEnumerable<(double Latitude, double Longitude)> coordinates = RandomValueProvider.GetCoordinates(count);
- string expected = RandomValueProvider.GetPolyline(count);
-
- // Act
- var result = _encoder.Encode(coordinates);
-
- // Assert
- Assert.AreEqual(expected.Length, result.Length);
- Assert.IsTrue(expected.Equals(result));
- }
-
- [TestMethod]
- public void Encode_StaticValue_ValidInput_Ok() {
- // Arrange
- IEnumerable<(double Latitude, double Longitude)> coordinates = StaticValueProvider.Valid.GetCoordinates();
- string expected = StaticValueProvider.Valid.GetPolyline();
-
- // Act
- var result = _encoder.Encode(coordinates);
-
- // Assert
- Assert.AreEqual(expected.Length, result.Length);
- Assert.IsTrue(expected.Equals(result));
- }
-
- public class PolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> {
- public PolylineEncoder()
- : base() { }
-
- public PolylineEncoder(PolylineEncodingOptions options)
- : base(options) { }
-
- protected override string CreatePolyline(ReadOnlyMemory polyline) => polyline.ToString();
- protected override double GetLatitude((double Latitude, double Longitude) coordinate) => coordinate.Latitude;
- protected override double GetLongitude((double Latitude, double Longitude) coordinate) => coordinate.Longitude;
- }
-}
diff --git a/tests/PolylineAlgorithm.Tests/CoordinateTest.cs b/tests/PolylineAlgorithm.Tests/CoordinateTest.cs
deleted file mode 100644
index b5d3eae1..00000000
--- a/tests/PolylineAlgorithm.Tests/CoordinateTest.cs
+++ /dev/null
@@ -1,253 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-///
-/// Tests for the type.
-///
-[TestClass]
-public class CoordinateTest {
- ///
- /// Provides test data for the method.
- ///
- public static IEnumerable Constructor_Valid_Parameters => [
- [90, 180],
- [-90, -180],
- [90, -180],
- [-90, 180],
- ];
-
- ///
- /// Provides test data for the method.
- ///
- public static IEnumerable Constructor_Invalid_Parameters => [
- [double.MinValue, 0],
- [double.MaxValue, 0],
- [double.NaN, 0],
- [double.PositiveInfinity, 0],
- [double.NegativeInfinity, 0],
- [0, double.MinValue],
- [0, double.MaxValue],
- [0, double.NaN],
- [0, double.PositiveInfinity],
- [0, double.NegativeInfinity]
- ];
-
- ///
- /// Tests the parameterless constructor of the class.
- ///
- [TestMethod]
- public void Constructor_Parameterless_Ok() {
- // Arrange
- bool @default = true;
- double latitude = 0d;
- double longitude = 0d;
-
- // Act
- Coordinate result = new();
-
- // Assert
- Assert.AreEqual(@default, result.IsDefault());
- Assert.AreEqual(latitude, result.Latitude);
- Assert.AreEqual(longitude, result.Longitude);
- }
-
- ///
- /// Tests the constructor with valid parameters.
- ///
- /// The latitude value.
- /// The longitude value.
- [TestMethod]
- [DynamicData(nameof(Constructor_Valid_Parameters))]
- public void Constructor_Valid_Parameters_Ok(double latitude, double longitude) {
- // Arrange & Act
- Coordinate coordinate = new(latitude, longitude);
-
- // Assert
- Assert.IsFalse(coordinate.IsDefault());
- Assert.AreEqual(latitude, coordinate.Latitude);
- Assert.AreEqual(longitude, coordinate.Longitude);
- }
-
- ///
- /// Tests the constructor with invalid parameters.
- ///
- /// The latitude value.
- /// The longitude value.
- [TestMethod]
- [DynamicData(nameof(Constructor_Invalid_Parameters))]
- public void Constructor_Invalid_Parameters_Ok(double latitude, double longitude) {
- // Arrange
- // Act
- static void New(double latitude, double longitude) => new Coordinate(latitude, longitude);
-
- // Assert
- Assert.ThrowsExactly(() => New(latitude, longitude));
- }
-
- ///
- /// Tests the method.
- ///
- /// The latitude value.
- /// The longitude value.
- [TestMethod]
- [DynamicData(nameof(Constructor_Valid_Parameters))]
- public void Deconstruct_Equals_Parameters(double latitude, double longitude) {
- // Arrange
- // Act
- Coordinate coordinate = new(latitude, longitude);
-
- // Assert
- Assert.AreEqual(latitude, coordinate.Latitude);
- Assert.AreEqual(longitude, coordinate.Longitude);
- }
-
- ///
- /// Tests the method with equal coordinates.
- ///
- /// The latitude value.
- /// The longitude value.
- [TestMethod]
- [DynamicData(nameof(Constructor_Valid_Parameters))]
- public void Equals_Coordinate_True(double latitude, double longitude) {
- // Arrange
- Coordinate @this = new(latitude, longitude);
- Coordinate other = new(latitude, longitude);
-
- // Act & Assert
- Assert.IsTrue(@this.Equals(other));
- }
-
- ///
- /// Tests the method with unequal coordinates.
- ///
- /// The latitude value.
- /// The longitude value.
- [TestMethod]
- [DynamicData(nameof(Constructor_Valid_Parameters))]
- public void Equals_Coordinate_False(double latitude, double longitude) {
- // Arrange
- Coordinate @this = new(latitude, longitude);
- Coordinate other = new(0, 0);
-
- // Act & Assert
- Assert.IsFalse(@this.Equals(other));
- }
-
- ///
- /// Tests the constructor with latitude out of range.
- ///
- [TestMethod]
- public void Constructor_Latitude_OutOfRange_Throws() {
- // Arrange & Act
- static void OverMaxLatitude() => new Coordinate(91, 0);
- static void UnderMinLatitude() => new Coordinate(-91, 0);
-
- // Assert
- Assert.ThrowsExactly(UnderMinLatitude);
- Assert.ThrowsExactly(OverMaxLatitude);
- }
-
- ///
- /// Tests the constructor with longitude out of range.
- ///
- [TestMethod]
- public void Constructor_Longitude_OutOfRange_Throws() {
- // Arrange & Act
- static void UnderMinLongitude() => new Coordinate(0, -181);
- static void OverMaxLongitude() => new Coordinate(0, 181);
-
- // Assert
- Assert.ThrowsExactly(UnderMinLongitude);
- Assert.ThrowsExactly(OverMaxLongitude);
- }
-
- ///
- /// Tests the constructor with boundary values.
- ///
- [TestMethod]
- public void Constructor_Boundary_Values_Ok() {
- // Arrange
- const int MinLatitude = -90;
- const int MinLongitude = -180;
- const int MaxLatitude = 90;
- const int MaxLongitude = 180;
-
- // Act
- Coordinate min = new(MinLatitude, MinLongitude);
- Coordinate max = new(MaxLatitude, MaxLongitude);
-
- // Assert
- Assert.AreEqual(MinLatitude, min.Latitude);
- Assert.AreEqual(-MaxLongitude, min.Longitude);
- Assert.AreEqual(MaxLatitude, max.Latitude);
- Assert.AreEqual(MaxLongitude, max.Longitude);
- }
-
- ///
- /// Tests the method with various cases.
- ///
- [TestMethod]
- public void Equals_Object_True_And_False() {
- // Arrange
- Coordinate coordinate = new(10, 20);
- object equalCoordinate = new Coordinate(10, 20);
- object notEqualCoordinate = new Coordinate(0, 0);
- object notCoordinate = "not a coordinate";
- object @null = null!;
-
- // Act & Assert
- Assert.IsTrue(coordinate.Equals(equalCoordinate));
- Assert.IsFalse(coordinate.Equals(notEqualCoordinate));
- Assert.IsFalse(coordinate.Equals(notCoordinate));
- Assert.IsFalse(coordinate.Equals(@null));
- }
-
- ///
- /// Tests the method for equal coordinates.
- ///
- [TestMethod]
- public void GetHashCode_Equal_For_Equal_Coordinates() {
- // Arrange
- Coordinate first = new(10, 20);
- Coordinate second = new(10, 20);
-
- // Act & Assert
- Assert.AreEqual(first.GetHashCode(), second.GetHashCode());
- }
-
- ///
- /// Tests the method for correct formatting.
- ///
- [TestMethod]
- public void ToString_Format_Ok() {
- /// Arrange
- var coordinate = new Coordinate(12.34, 56.78);
-
- // Act & Assert
- Assert.Contains("Latitude: 12.34", coordinate.ToString());
- Assert.Contains("Longitude: 56.78", coordinate.ToString());
- }
-
- ///
- /// Tests the equality operators for the type.
- ///
- [TestMethod]
- public void Equality_Operators_Ok() {
- // Arrange
- Coordinate coordinate = new(10, 20);
- Coordinate equalCoordinate = new(10, 20);
- Coordinate notEqualCoordinate = new(0, 0);
-
- // Act & Assert
- Assert.IsTrue(coordinate == equalCoordinate);
- Assert.IsFalse(coordinate != equalCoordinate);
- Assert.IsTrue(coordinate != notEqualCoordinate);
- Assert.IsFalse(coordinate == notEqualCoordinate);
- }
-}
\ No newline at end of file
diff --git a/tests/PolylineAlgorithm.Tests/CoordinateTests.cs b/tests/PolylineAlgorithm.Tests/CoordinateTests.cs
new file mode 100644
index 00000000..13eb08be
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/CoordinateTests.cs
@@ -0,0 +1,1328 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests;
+
+using PolylineAlgorithm.Tests.Properties;
+using System;
+
+///
+/// Tests for .
+///
+[TestClass]
+[TestCategory(Category.Unit)]
+public sealed class CoordinateTests {
+ ///
+ /// Tests that default constructor creates coordinate with zero latitude and longitude.
+ ///
+ [TestMethod]
+
+ public void Default_Constructor_Creates_Coordinate_With_Zero_Values() {
+ // Arrange & Act
+ Coordinate coordinate = new();
+
+ // Assert
+ Assert.AreEqual(0.0, coordinate.Latitude);
+ Assert.AreEqual(0.0, coordinate.Longitude);
+ }
+
+ ///
+ /// Tests that parameterized constructor creates coordinate with specified values.
+ ///
+ [TestMethod]
+ [DataRow(0.0, 0.0)]
+ [DataRow(90.0, 0.0)]
+ [DataRow(-90.0, 0.0)]
+ [DataRow(90.0, 180.0)]
+ [DataRow(-90.0, 180.0)]
+ [DataRow(90.0, -180.0)]
+ [DataRow(-90.0, -180.0)]
+ [DataRow(0.0, 180.0)]
+ [DataRow(0.0, -180.0)]
+ public void Constructor_With_Valid_Values_Creates_Instance_With_Specified_Values(double latitude, double longitude) {
+ // Act
+ Coordinate coordinate = new(latitude, longitude);
+
+ // Assert
+ Assert.AreEqual(latitude, coordinate.Latitude);
+ Assert.AreEqual(longitude, coordinate.Longitude);
+ }
+
+ ///
+ /// Tests that constructor throws ArgumentOutOfRangeException when latitude is greater than 90.
+ ///
+ [TestMethod]
+ [DataRow(90.0000000001, 0.0, "latitude")]
+ [DataRow(-90.0000000001, 0.0, "latitude")]
+ [DataRow(double.NaN, 0.0, "latitude")]
+ [DataRow(double.PositiveInfinity, 0.0, "latitude")]
+ [DataRow(double.NegativeInfinity, 0.0, "latitude")]
+ [DataRow(0.0, 180.0000000001, "longitude")]
+ [DataRow(0.0, -180.0000000001, "longitude")]
+ [DataRow(0.0, double.NaN, "longitude")]
+ [DataRow(0.0, double.PositiveInfinity, "longitude")]
+ [DataRow(0.0, double.NegativeInfinity, "longitude")]
+ public void Constructor_With_Invalid_Values_Throws_ArgumentOutOfRangeException(double latitude, double longitude, string paramName) {
+ // Act
+ ArgumentOutOfRangeException exception =
+ Assert.ThrowsExactly(() => new Coordinate(latitude, longitude));
+
+ // Assert
+ Assert.AreEqual(paramName, exception.ParamName);
+ }
+
+ ///
+ /// Tests that IsDefault returns true for default constructed coordinate.
+ ///
+ [TestMethod]
+ public void IsDefault_With_Default_Coordinate_Instance_Returns_True() {
+ // Arrange
+ Coordinate coordinate = default;
+
+ // Act
+ bool result = coordinate.IsDefault();
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that IsDefault returns true for coordinate with zero values.
+ ///
+ [TestMethod]
+
+ public void IsDefault_With_Zero_Values_Returns_True() {
+ // Arrange
+ Coordinate coordinate = new(0.0, 0.0);
+
+ // Act
+ bool result = coordinate.IsDefault();
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that IsDefault returns false for coordinate with non-zero latitude.
+ ///
+ [TestMethod]
+ [DataRow(1.0, 0.0)]
+ [DataRow(0.0, 1.0)]
+ [DataRow(1.0, 1.0)]
+ public void IsDefault_With_Non_Zero_Values_Returns_False(double latitude, double longitude) {
+ // Arrange
+ Coordinate coordinate = new(latitude, longitude);
+
+ // Act
+ bool result = coordinate.IsDefault();
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals returns true when object is same coordinate.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Identical_Coordinate_As_Object_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ object coordinate2 = new Coordinate(45.5, -122.5);
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals returns false when object is null.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Null_Object_Returns_False() {
+ // Arrange
+ Coordinate coordinate = new(45.5, -122.5);
+
+ // Act
+ bool result = coordinate.Equals((object?)null);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals returns false when object is of different type.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Different_Type_Returns_False() {
+ // Arrange
+ Coordinate coordinate = new(45.5, -122.5);
+ object otherObject = "not a coordinate";
+
+ // Act
+ bool result = coordinate.Equals(otherObject);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals returns false when object is coordinate with different values.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Different_Coordinate_As_Object_Returns_False() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ object coordinate2 = new Coordinate(45.5, -122.6);
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals returns true when both coordinates have same values.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Identical_Coordinate_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5, -122.5);
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals returns false when latitudes differ.
+ ///
+ [TestMethod]
+ [DataRow(45.6, -122.5)]
+ [DataRow(45.5, -122.6)]
+ [DataRow(46.5, -121.5)]
+ public void Equals_With_Different_Values_Returns_False(double latitude, double longitude) {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(latitude, longitude);
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals returns true for default coordinates.
+ ///
+ [TestMethod]
+ public void Equals_With_Default_Coordinates_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new();
+ Coordinate coordinate2 = new();
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals with tolerance returns true when coordinates are identical.
+ ///
+ [TestMethod]
+ [DataRow(45.5, -122.5, 0.1)]
+ [DataRow(45.5, -122.5, 0.001)]
+ [DataRow(45.5, -122.5, 0.0000001)]
+ [DataRow(45.5009, -122.5009, 0.001)]
+ [DataRow(45.50099, -122.50099, 0.001)]
+ [DataRow(45.4991, -122.4991, 0.001)]
+ public void Equals_Within_Tolerance_Returns_True(double latitude, double longitude, double tolerance) {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(latitude, longitude);
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals with tolerance returns false when latitude difference exceeds tolerance.
+ ///
+ [TestMethod]
+ [DataRow(45.7, -122.5, 0.1)]
+ [DataRow(45.5, -122.7, 0.1)]
+ [DataRow(45.7, -122.7, 0.1)]
+ [DataRow(45.502, -122.5, 0.001)]
+ [DataRow(45.5, -122.502, 0.001)]
+ [DataRow(45.5, -122.5000002, 0.0000001)]
+ [DataRow(45.5000002, -122.5, 0.0000001)]
+ public void Equals_With_Exceeding_Tolerance_Returns_False(double latitude, double longitude, double tolerance) {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(latitude, longitude);
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that GetHashCode returns same value for identical coordinates.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_With_Identical_Coordinates_Returns_Identical_Hashcode() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5, -122.5);
+
+ // Act
+ int hash1 = coordinate1.GetHashCode();
+ int hash2 = coordinate2.GetHashCode();
+
+ // Assert
+ Assert.AreEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that GetHashCode returns different values for different coordinates.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_With_Different_Coordinates_Returns_Different_Values() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.6, -122.6);
+
+ // Act
+ int hash1 = coordinate1.GetHashCode();
+ int hash2 = coordinate2.GetHashCode();
+
+ // Assert
+ Assert.AreNotEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that GetHashCode returns consistent value for same coordinate.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_Returns_Identical_Value() {
+ // Arrange
+ Coordinate coordinate = new(45.5, -122.5);
+
+ // Act
+ int hash1 = coordinate.GetHashCode();
+ int hash2 = coordinate.GetHashCode();
+
+ // Assert
+ Assert.AreEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that GetHashCode returns same value for default coordinates.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_With_Default_Coordinates_Returns_Identical_Value() {
+ // Arrange
+ Coordinate coordinate1 = new();
+ Coordinate coordinate2 = new(0.0, 0.0);
+
+ // Act
+ int hash1 = coordinate1.GetHashCode();
+ int hash2 = coordinate2.GetHashCode();
+
+ // Assert
+ Assert.AreEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that ToString returns expected format with positive coordinates.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Positive_Coordinates_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(45.5, 122.5);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.AreEqual("{ Latitude: 45.5, Longitude: 122.5 }", result);
+ }
+
+ ///
+ /// Tests that ToString returns expected format with negative coordinates.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Negative_Coordinates_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(-45.5, -122.5);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.AreEqual("{ Latitude: -45.5, Longitude: -122.5 }", result);
+ }
+
+ ///
+ /// Tests that ToString returns expected format for default coordinate.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Default_Coordinate_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new();
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.AreEqual("{ Latitude: 0, Longitude: 0 }", result);
+ }
+
+ ///
+ /// Tests that ToString returns expected format with mixed sign coordinates.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Mixed_Sign_Coordinates_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(45.5, -122.5);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.AreEqual("{ Latitude: 45.5, Longitude: -122.5 }", result);
+ }
+
+ ///
+ /// Tests that ToString uses invariant culture formatting.
+ ///
+ [TestMethod]
+
+ public void ToString_Uses_Invariant_Culture_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(45.123456789, -122.987654321);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.IsTrue(result.Contains("45.123456789", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("-122.987654321", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString returns expected format with boundary values.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Boundary_Values_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(90.0, 180.0);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.AreEqual("{ Latitude: 90, Longitude: 180 }", result);
+ }
+
+ ///
+ /// Tests that ValidateValue accepts valid value within specified range.
+ ///
+ [TestMethod]
+
+ public void Validate_Value_With_Valid_Value_Does_Not_Throw() {
+ // Arrange
+ double value = 50.0;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ Coordinate.Validator.ValidateValue(value, min, max, paramName);
+ }
+
+ ///
+ /// Tests that ValidateValue accepts value at minimum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Minimum_Boundary_Does_Not_Throw() {
+ // Arrange
+ double value = 0.0;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ Coordinate.Validator.ValidateValue(value, min, max, paramName);
+ }
+
+ ///
+ /// Tests that ValidateValue accepts value at maximum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Maximum_Boundary_Does_Not_Throw() {
+ // Arrange
+ double value = 100.0;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ Coordinate.Validator.ValidateValue(value, min, max, paramName);
+ }
+
+ ///
+ /// Tests that ValidateValue throws ArgumentOutOfRangeException when value is below minimum.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Value_Below_Minimum_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double value = -0.1;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateValue(value, min, max, paramName));
+ Assert.AreEqual(paramName, exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateValue throws ArgumentOutOfRangeException when value exceeds maximum.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Value_Above_Maximum_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double value = 100.1;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateValue(value, min, max, paramName));
+ Assert.AreEqual(paramName, exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateValue throws ArgumentOutOfRangeException when value is NaN.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_NaN_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double value = double.NaN;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateValue(value, min, max, paramName));
+ Assert.AreEqual(paramName, exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateValue throws ArgumentOutOfRangeException when value is positive infinity.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Positive_Infinity_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double value = double.PositiveInfinity;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateValue(value, min, max, paramName));
+ Assert.AreEqual(paramName, exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateValue throws ArgumentOutOfRangeException when value is negative infinity.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Negative_Infinity_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double value = double.NegativeInfinity;
+ double min = 0.0;
+ double max = 100.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateValue(value, min, max, paramName));
+ Assert.AreEqual(paramName, exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateValue accepts negative value within negative range.
+ ///
+ [TestMethod]
+
+ public void ValidateValue_With_Negative_Value_In_Negative_Range_Does_Not_Throw() {
+ // Arrange
+ double value = -50.0;
+ double min = -100.0;
+ double max = -10.0;
+ string paramName = "testParam";
+
+ // Act & Assert
+ Coordinate.Validator.ValidateValue(value, min, max, paramName);
+ }
+
+ ///
+ /// Tests that Validate accepts valid latitude and longitude.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Valid_Latitude_And_Longitude_Does_Not_Throw() {
+ // Arrange
+ double latitude = 45.5;
+ double longitude = -122.5;
+
+ // Act & Assert
+ Coordinate.Validator.Validate(latitude, longitude);
+ }
+
+ ///
+ /// Tests that Validate accepts boundary values.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Boundary_Values_Does_Not_Throw() {
+ // Arrange
+ double latitude = 90.0;
+ double longitude = 180.0;
+
+ // Act & Assert
+ Coordinate.Validator.Validate(latitude, longitude);
+ }
+
+ ///
+ /// Tests that Validate accepts minimum boundary values.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Minimum_Boundary_Values_Does_Not_Throw() {
+ // Arrange
+ double latitude = -90.0;
+ double longitude = -180.0;
+
+ // Act & Assert
+ Coordinate.Validator.Validate(latitude, longitude);
+ }
+
+ ///
+ /// Tests that Validate throws ArgumentOutOfRangeException when latitude is invalid.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Invalid_Latitude_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = 91.0;
+ double validLongitude = 0.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.Validate(invalidLatitude, validLongitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Validate throws ArgumentOutOfRangeException when longitude is invalid.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Invalid_Longitude_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double validLatitude = 0.0;
+ double invalidLongitude = 181.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.Validate(validLatitude, invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Validate throws ArgumentOutOfRangeException when latitude is NaN.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Latitude_NaN_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = double.NaN;
+ double validLongitude = 0.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.Validate(invalidLatitude, validLongitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Validate throws ArgumentOutOfRangeException when longitude is NaN.
+ ///
+ [TestMethod]
+
+ public void Validate_With_Longitude_NaN_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double validLatitude = 0.0;
+ double invalidLongitude = double.NaN;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.Validate(validLatitude, invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLatitude accepts valid latitude within range.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Valid_Latitude_Does_Not_Throw() {
+ // Arrange
+ double validLatitude = 45.5;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLatitude(validLatitude);
+ }
+
+ ///
+ /// Tests that ValidateLatitude accepts latitude at minimum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Minimum_Boundary_Does_Not_Throw() {
+ // Arrange
+ double validLatitude = -90.0;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLatitude(validLatitude);
+ }
+
+ ///
+ /// Tests that ValidateLatitude accepts latitude at maximum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Maximum_Boundary_Does_Not_Throw() {
+ // Arrange
+ double validLatitude = 90.0;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLatitude(validLatitude);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws ArgumentOutOfRangeException when latitude exceeds maximum.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Latitude_Greater_Than_90_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = 91.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws ArgumentOutOfRangeException when latitude is below minimum.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Latitude_Less_Than_Negative_90_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = -91.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws ArgumentOutOfRangeException when latitude is NaN.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_NaN_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = double.NaN;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws ArgumentOutOfRangeException when latitude is positive infinity.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Positive_Infinity_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = double.PositiveInfinity;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws ArgumentOutOfRangeException when latitude is negative infinity.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Negative_Infinity_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = double.NegativeInfinity;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude accepts valid longitude within range.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Valid_Longitude_Does_Not_Throw() {
+ // Arrange
+ double validLongitude = -122.5;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLongitude(validLongitude);
+ }
+
+ ///
+ /// Tests that ValidateLongitude accepts longitude at minimum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Minimum_Boundary_Does_Not_Throw() {
+ // Arrange
+ double validLongitude = -180.0;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLongitude(validLongitude);
+ }
+
+ ///
+ /// Tests that ValidateLongitude accepts longitude at maximum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Maximum_Boundary_Does_Not_Throw() {
+ // Arrange
+ double validLongitude = 180.0;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLongitude(validLongitude);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws ArgumentOutOfRangeException when longitude exceeds maximum.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Longitude_Greater_Than_180_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = 181.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws ArgumentOutOfRangeException when longitude is below minimum.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Longitude_Less_Than_Negative_180_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = -181.0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws ArgumentOutOfRangeException when longitude is NaN.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_NaN_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = double.NaN;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws ArgumentOutOfRangeException when longitude is positive infinity.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Positive_Infinity_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = double.PositiveInfinity;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws ArgumentOutOfRangeException when longitude is negative infinity.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Negative_Infinity_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = double.NegativeInfinity;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Equals with zero tolerance returns false even for identical coordinates.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Zero_Tolerance_Returns_False() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5, -122.5);
+ double tolerance = 0.0;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals with zero tolerance returns false for non-identical coordinates.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Zero_Tolerance_And_Different_Coordinates_Returns_False() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5000001, -122.5);
+ double tolerance = 0.0;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals with difference exactly equal to tolerance returns true.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Difference_Exactly_Equal_To_Tolerance_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.501, -122.5);
+ double tolerance = 0.001;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals with negative tolerance behaves like absolute tolerance.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Negative_Tolerance_Returns_False() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5, -122.5);
+ double tolerance = -0.001;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests that Equals with large tolerance returns true for widely different coordinates.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Large_Tolerance_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(50.0, -120.0);
+ double tolerance = 10.0;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals with tolerance works correctly at extreme latitude boundaries.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Extreme_Boundary_Latitudes_Returns_Expected_Result() {
+ // Arrange
+ Coordinate coordinate1 = new(90.0, 0.0);
+ Coordinate coordinate2 = new(89.9991, 0.0);
+ double tolerance = 0.001;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that Equals with tolerance works correctly at extreme longitude boundaries.
+ ///
+ [TestMethod]
+
+ public void Equals_With_Tolerance_With_Extreme_Boundary_Longitudes_Returns_Expected_Result() {
+ // Arrange
+ Coordinate coordinate1 = new(0.0, 180.0);
+ Coordinate coordinate2 = new(0.0, 179.9991);
+ double tolerance = 0.001;
+
+ // Act
+ bool result = coordinate1.Equals(coordinate2, tolerance);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests that GetHashCode returns different values for coordinates with slight differences.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_With_Slightly_Different_Coordinates_Returns_Different_Values() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5000001, -122.5);
+
+ // Act
+ int hash1 = coordinate1.GetHashCode();
+ int hash2 = coordinate2.GetHashCode();
+
+ // Assert
+ Assert.AreNotEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that GetHashCode works with boundary latitude values.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_With_Boundary_Latitude_Values_Returns_Value() {
+ // Arrange
+ Coordinate coordinate1 = new(90.0, 0.0);
+ Coordinate coordinate2 = new(-90.0, 0.0);
+
+ // Act
+ int hash1 = coordinate1.GetHashCode();
+ int hash2 = coordinate2.GetHashCode();
+
+ // Assert
+ Assert.AreNotEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that GetHashCode works with boundary longitude values.
+ ///
+ [TestMethod]
+
+ public void GetHashCode_With_Boundary_Longitude_Values_Returns_Value() {
+ // Arrange
+ Coordinate coordinate1 = new(0.0, 180.0);
+ Coordinate coordinate2 = new(0.0, -180.0);
+
+ // Act
+ int hash1 = coordinate1.GetHashCode();
+ int hash2 = coordinate2.GetHashCode();
+
+ // Assert
+ Assert.AreNotEqual(hash1, hash2);
+ }
+
+ ///
+ /// Tests that ToString handles very small non-zero values correctly.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Very_Small_Values_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(0.0000001, -0.0000001);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.IsTrue(result.Contains("1E-07", StringComparison.Ordinal) || result.Contains("0.0000001", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("-1E-07", StringComparison.Ordinal) || result.Contains("-0.0000001", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString handles high precision decimal values correctly.
+ ///
+ [TestMethod]
+
+ public void ToString_With_High_Precision_Values_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(45.123456789012345, -122.987654321098765);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.IsTrue(result.Contains("45.123456789", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("-122.98765432", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString formats negative zero as negative zero.
+ ///
+ [TestMethod]
+
+ public void ToString_With_Negative_Zero_Returns_Expected_Format() {
+ // Arrange
+ Coordinate coordinate = new(-0.0, -0.0);
+
+ // Act
+ string result = coordinate.ToString();
+
+ // Assert
+ Assert.IsTrue(result.Contains("Latitude: ") && result.Contains("Longitude: "));
+ }
+
+ ///
+ /// Tests that ValidateLatitude accepts zero value.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Zero_Does_Not_Throw() {
+ // Arrange
+ double validLatitude = 0.0;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLatitude(validLatitude);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws with value just beyond maximum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Value_Just_Beyond_Maximum_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = 90.000001;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLatitude throws with value just beyond minimum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLatitude_With_Value_Just_Beyond_Minimum_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLatitude = -90.000001;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLatitude(invalidLatitude));
+ Assert.AreEqual("latitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude accepts zero value.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Zero_Does_Not_Throw() {
+ // Arrange
+ double validLongitude = 0.0;
+
+ // Act & Assert
+ Coordinate.Validator.ValidateLongitude(validLongitude);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws with value just beyond maximum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Value_Just_Beyond_Maximum_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = 180.000001;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests that ValidateLongitude throws with value just beyond minimum boundary.
+ ///
+ [TestMethod]
+
+ public void ValidateLongitude_With_Value_Just_Beyond_Minimum_Throws_ArgumentOutOfRangeException() {
+ // Arrange
+ double invalidLongitude = -180.000001;
+
+ // Act & Assert
+ ArgumentOutOfRangeException exception = Assert.ThrowsExactly(() =>
+ Coordinate.Validator.ValidateLongitude(invalidLongitude));
+ Assert.AreEqual("longitude", exception.ParamName);
+ }
+
+ ///
+ /// Tests the equality (==) operator returns true for identical coordinates.
+ ///
+ [TestMethod]
+
+ public void Equality_Operator_With_Identical_Coordinates_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5, -122.5);
+
+ // Act
+ bool result = coordinate1 == coordinate2;
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ ///
+ /// Tests the equality (==) operator returns false for different coordinates.
+ ///
+ [TestMethod]
+
+ public void Equality_Operator_With_Different_Coordinates_Returns_False() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.6, -122.5);
+
+ // Act
+ bool result = coordinate1 == coordinate2;
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests the inequality (!=) operator returns false for identical coordinates.
+ ///
+ [TestMethod]
+
+ public void Inequality_Operator_With_Identical_Coordinates_Returns_False() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.5, -122.5);
+
+ // Act
+ bool result = coordinate1 != coordinate2;
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ ///
+ /// Tests the inequality (!=) operator returns true for different coordinates.
+ ///
+ [TestMethod]
+
+ public void Inequality_Operator_With_Different_Coordinates_Returns_True() {
+ // Arrange
+ Coordinate coordinate1 = new(45.5, -122.5);
+ Coordinate coordinate2 = new(45.6, -122.5);
+
+ // Act
+ bool result = coordinate1 != coordinate2;
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs
new file mode 100644
index 00000000..f45659a5
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs
@@ -0,0 +1,163 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Extensions;
+
+using PolylineAlgorithm;
+using PolylineAlgorithm.Abstraction;
+using PolylineAlgorithm.Extensions;
+using PolylineAlgorithm.Tests.Properties;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class PolylineDecoderExtensionsTests
+{
+ private sealed class TestPolylineDecoder : IPolylineDecoder
+ {
+ public Polyline? LastPolyline { get; private set; }
+ private readonly IEnumerable _coordinatesToReturn;
+
+ public TestPolylineDecoder(IEnumerable coordinatesToReturn)
+ {
+ _coordinatesToReturn = coordinatesToReturn;
+ }
+
+ public IEnumerable Decode(Polyline polyline)
+ {
+ LastPolyline = polyline;
+ return _coordinatesToReturn;
+ }
+ }
+
+ ///
+ /// Tests that Decode with string parameter throws ArgumentNullException when decoder is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Decode_StringWithNullDecoder_ThrowsArgumentNullException()
+ {
+ // Arrange
+ IPolylineDecoder? decoder = null;
+ string polyline = "test";
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => decoder!.Decode(polyline));
+ Assert.AreEqual("decoder", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Decode with string parameter calls decoder with polyline from string.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Decode_StringWithValidDecoder_CallsDecoderWithPolylineFromString()
+ {
+ // Arrange
+ string polylineString = "_p~iF~ps|U_ulLnnqC_mqNvxq`@";
+ Coordinate[] expectedCoordinates =
+ [
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95),
+ new Coordinate(43.252, -126.453)
+ ];
+ var decoder = new TestPolylineDecoder(expectedCoordinates);
+
+ // Act
+ IEnumerable result = decoder.Decode(polylineString);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Coordinate[] coordinates = result.ToArray();
+ Assert.AreEqual(3, coordinates.Length);
+ Assert.AreSame(decoder.LastPolyline, Polyline.FromString(polylineString));
+ }
+
+ ///
+ /// Tests that Decode with char array parameter throws ArgumentNullException when decoder is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Decode_CharArrayWithNullDecoder_ThrowsArgumentNullException()
+ {
+ // Arrange
+ IPolylineDecoder? decoder = null;
+ char[] polyline = ['t', 'e', 's', 't'];
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => decoder!.Decode(polyline));
+ Assert.AreEqual("decoder", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Decode with char array parameter calls decoder with polyline from char array.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Decode_CharArrayWithValidDecoder_CallsDecoderWithPolylineFromCharArray()
+ {
+ // Arrange
+ char[] polylineChars = ['_', 'p', '~', 'i', 'F', '~', 'p', 's', '|', 'U'];
+ Coordinate[] expectedCoordinates =
+ [
+ new Coordinate(38.5, -120.2)
+ ];
+ var decoder = new TestPolylineDecoder(expectedCoordinates);
+
+ // Act
+ IEnumerable result = decoder.Decode(polylineChars);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Coordinate[] coordinates = result.ToArray();
+ Assert.AreEqual(1, coordinates.Length);
+ Assert.AreEqual(decoder.LastPolyline, Polyline.FromCharArray(polylineChars));
+ }
+
+ ///
+ /// Tests that Decode with ReadOnlyMemory parameter throws ArgumentNullException when decoder is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Decode_ReadOnlyMemoryWithNullDecoder_ThrowsArgumentNullException()
+ {
+ // Arrange
+ IPolylineDecoder? decoder = null;
+ ReadOnlyMemory polyline = "test".AsMemory();
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => decoder!.Decode(polyline));
+ Assert.AreEqual("decoder", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Decode with ReadOnlyMemory parameter calls decoder with polyline from memory.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Decode_ReadOnlyMemoryWithValidDecoder_CallsDecoderWithPolylineFromMemory()
+ {
+ // Arrange
+ ReadOnlyMemory polylineMemory = "_p~iF~ps|U".AsMemory();
+ Coordinate[] expectedCoordinates =
+ [
+ new Coordinate(38.5, -120.2)
+ ];
+ var decoder = new TestPolylineDecoder(expectedCoordinates);
+
+ // Act
+ IEnumerable result = decoder.Decode(polylineMemory);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Coordinate[] coordinates = result.ToArray();
+ Assert.AreEqual(1, coordinates.Length);
+ Assert.AreEqual(decoder.LastPolyline, Polyline.FromMemory(polylineMemory));
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs
new file mode 100644
index 00000000..14931071
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs
@@ -0,0 +1,170 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Extensions;
+
+using PolylineAlgorithm;
+using PolylineAlgorithm.Abstraction;
+using PolylineAlgorithm.Extensions;
+using PolylineAlgorithm.Tests.Properties;
+using System;
+using System.Collections.Generic;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class PolylineEncoderExtensionsTests
+{
+ ///
+ /// Tests that Encode with List parameter throws ArgumentNullException when encoder is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ListWithNullEncoder_ThrowsArgumentNullException()
+ {
+ // Arrange
+ IPolylineEncoder? encoder = null;
+ List coordinates = [new Coordinate(38.5, -120.2)];
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => encoder!.Encode(coordinates));
+ Assert.AreEqual("encoder", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Encode with List parameter throws ArgumentNullException when coordinates is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ListWithNullCoordinates_ThrowsArgumentNullException()
+ {
+ // Arrange
+ PolylineEncoder encoder = new();
+ List? coordinates = null;
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => encoder.Encode(coordinates!));
+ Assert.AreEqual("coordinates", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Encode with List parameter calls encoder with coordinates from list.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ListWithValidEncoder_CallsEncoderWithCoordinatesFromList()
+ {
+ // Arrange
+ List coordinates =
+ [
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95),
+ new Coordinate(43.252, -126.453)
+ ];
+ Polyline expectedPolyline = Polyline.FromString("_p~iF~ps|U_ulLnnqC_mqNvxq`@");
+ PolylineEncoder encoder = new();
+
+ // Act
+ Polyline result = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual(expectedPolyline, result);
+ }
+
+ ///
+ /// Tests that Encode with List parameter handles empty list.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ListWithEmptyList_CallsEncoderWithEmptySpan()
+ {
+ // Arrange
+ List coordinates = [];
+ Polyline expectedPolyline = Polyline.FromString("");
+ PolylineEncoder encoder = new();
+
+ // Act
+ Polyline result = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual(expectedPolyline, result);
+ }
+
+ ///
+ /// Tests that Encode with array parameter throws ArgumentNullException when encoder is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ArrayWithNullEncoder_ThrowsArgumentNullException()
+ {
+ // Arrange
+ IPolylineEncoder? encoder = null;
+ Coordinate[] coordinates = [new Coordinate(38.5, -120.2)];
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => encoder!.Encode(coordinates));
+ Assert.AreEqual("encoder", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Encode with array parameter throws ArgumentNullException when coordinates is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ArrayWithNullCoordinates_ThrowsArgumentNullException()
+ {
+ // Arrange
+ PolylineEncoder encoder = new();
+ Coordinate[]? coordinates = null;
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(() => PolylineEncoderExtensions.Encode(encoder, coordinates!));
+ Assert.AreEqual("coordinates", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Encode with array parameter calls encoder with coordinates from array.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ArrayWithValidEncoder_CallsEncoderWithCoordinatesFromArray()
+ {
+ // Arrange
+ Coordinate[] coordinates =
+ [
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95),
+ new Coordinate(43.252, -126.453)
+ ];
+ Polyline expectedPolyline = Polyline.FromString("_p~iF~ps|U_ulLnnqC_mqNvxq`@");
+ PolylineEncoder encoder = new();
+
+ // Act
+ Polyline result = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual(expectedPolyline, result);
+ }
+
+ ///
+ /// Tests that Encode with array parameter handles empty array.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Encode_ArrayWithEmptyArray_CallsEncoderWithEmptySpan()
+ {
+ // Arrange
+ Coordinate[] coordinates = [];
+ Polyline expectedPolyline = Polyline.FromString("");
+ PolylineEncoder encoder = new();
+
+ // Act
+ Polyline result = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual(expectedPolyline, result);
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Fakes/FakeLoggerFactory.cs b/tests/PolylineAlgorithm.Tests/Fakes/FakeLoggerFactory.cs
deleted file mode 100644
index 4b7e71e0..00000000
--- a/tests/PolylineAlgorithm.Tests/Fakes/FakeLoggerFactory.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests.Fakes;
-
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Testing;
-
-internal class FakeLoggerFactory : ILoggerFactory {
- private bool _isDisposed;
- public FakeLoggerFactory(FakeLoggerProvider loggerProvider) {
- Provider = loggerProvider ?? throw new ArgumentNullException(nameof(loggerProvider));
- }
-
- public ILoggerProvider Provider { get; private set; }
-
- public void AddProvider(ILoggerProvider provider) {
- Provider = provider;
- }
-
- public ILogger CreateLogger(string categoryName) {
- return Provider.CreateLogger(categoryName);
- }
-
- protected virtual void Dispose(bool disposing) {
- if (!_isDisposed) {
- if (disposing) {
-
- }
-
- _isDisposed = true;
- }
- }
-
- public void Dispose() {
- Dispose(disposing: true);
- GC.SuppressFinalize(this);
- }
-}
\ No newline at end of file
diff --git a/tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs b/tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs
new file mode 100644
index 00000000..5b05c0eb
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs
@@ -0,0 +1,302 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Internal;
+
+using PolylineAlgorithm.Internal;
+using PolylineAlgorithm.Tests.Properties;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class CoordinateDeltaTests {
+ ///
+ /// Tests that default constructor initializes delta values to zero.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Constructor_Default_Initializes_Latitude_And_Longitude_To_Zero() {
+ // Act
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Assert
+ Assert.AreEqual(0, delta.Latitude);
+ Assert.AreEqual(0, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with positive values calculates correct delta from zero.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Positive_Values_Calculates_Delta_From_Zero() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(10, 20);
+
+ // Assert
+ Assert.AreEqual(10, delta.Latitude);
+ Assert.AreEqual(20, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with negative values calculates correct delta from zero.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Negative_Values_Calculates_Delta_From_Zero() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(-50, -100);
+
+ // Assert
+ Assert.AreEqual(-50, delta.Latitude);
+ Assert.AreEqual(-100, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with zero values keeps delta at zero.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Zero_Values_Keeps_Delta_At_Zero() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(0, 0);
+
+ // Assert
+ Assert.AreEqual(0, delta.Latitude);
+ Assert.AreEqual(0, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next called multiple times calculates delta from previous value.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_Called_Multiple_Times_Calculates_Delta_From_Previous_Value() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(10, 20);
+ delta.Next(15, 30);
+
+ // Assert
+ Assert.AreEqual(5, delta.Latitude);
+ Assert.AreEqual(10, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with decreasing values calculates negative delta.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Decreasing_Values_Calculates_Negative_Delta() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(100, 200);
+ delta.Next(50, 150);
+
+ // Assert
+ Assert.AreEqual(-50, delta.Latitude);
+ Assert.AreEqual(-50, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with same values as previous calculates zero delta.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Same_Values_As_Previous_Calculates_Zero_Delta() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(42, 84);
+ delta.Next(42, 84);
+
+ // Assert
+ Assert.AreEqual(0, delta.Latitude);
+ Assert.AreEqual(0, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with maximum integer values calculates correct delta.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Maximum_Integer_Values_Calculates_Correct_Delta() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(int.MaxValue, int.MaxValue);
+
+ // Assert
+ Assert.AreEqual(int.MaxValue, delta.Latitude);
+ Assert.AreEqual(int.MaxValue, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with minimum integer values calculates correct delta.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Minimum_Integer_Values_Calculates_Correct_Delta() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(int.MinValue, int.MinValue);
+
+ // Assert
+ Assert.AreEqual(int.MinValue, delta.Latitude);
+ Assert.AreEqual(int.MinValue, delta.Longitude);
+ }
+
+ ///
+ /// Tests that Next with mixed positive and negative values calculates correct delta.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Next_With_Mixed_Positive_And_Negative_Values_Calculates_Correct_Delta() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ delta.Next(-50, 100);
+ delta.Next(25, -75);
+
+ // Assert
+ Assert.AreEqual(75, delta.Latitude);
+ Assert.AreEqual(-175, delta.Longitude);
+ }
+
+ ///
+ /// Tests that ToString with default constructor returns formatted string with zeros.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ToString_With_Default_Constructor_Returns_Formatted_String_With_Zeros() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+
+ // Act
+ string result = delta.ToString();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("Coordinate", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("Delta", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("Latitude", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("Longitude", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains('0'));
+ }
+
+ ///
+ /// Tests that ToString after Next returns formatted string with correct values.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ToString_After_Next_Returns_Formatted_String_With_Correct_Values() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+ delta.Next(42, 84);
+
+ // Act
+ string result = delta.ToString();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("42", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("84", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString after multiple Next calls returns formatted string with latest values.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ToString_After_Multiple_Next_Calls_Returns_Formatted_String_With_Latest_Values() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+ delta.Next(10, 20);
+ delta.Next(30, 50);
+
+ // Act
+ string result = delta.ToString();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("30", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("50", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("20", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString with negative values returns formatted string with negative signs.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ToString_With_Negative_Values_Returns_Formatted_String_With_Negative_Signs() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+ delta.Next(-100, -200);
+
+ // Act
+ string result = delta.ToString();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("-100", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("-200", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString with maximum integer values returns formatted string.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ToString_With_Maximum_Integer_Values_Returns_Formatted_String() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+ delta.Next(int.MaxValue, int.MaxValue);
+
+ // Act
+ string result = delta.ToString();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains(int.MaxValue.ToString(System.Globalization.CultureInfo.InvariantCulture), StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that ToString with minimum integer values returns formatted string.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ToString_With_Minimum_Integer_Values_Returns_Formatted_String() {
+ // Arrange
+ CoordinateDelta delta = new CoordinateDelta();
+ delta.Next(int.MinValue, int.MinValue);
+
+ // Act
+ string result = delta.ToString();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains(int.MinValue.ToString(System.Globalization.CultureInfo.InvariantCulture), StringComparison.Ordinal));
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Internal/CoordinateVarianceTest.cs b/tests/PolylineAlgorithm.Tests/Internal/CoordinateVarianceTest.cs
deleted file mode 100644
index 84d1be42..00000000
--- a/tests/PolylineAlgorithm.Tests/Internal/CoordinateVarianceTest.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests.Internal;
-
-using PolylineAlgorithm.Internal;
-
-[TestClass]
-public class CoordinateVarianceTests {
- public static IEnumerable<(int Latitude, int Longitude)> Coordinates => [
- (0, 0),
- (-10, -10),
- (10, -10),
- (-10, 10),
- (10, 10)
- ];
-
- public static IEnumerable<((int Latitude, int Longitude) Initial, (int Latitude, int Longitude) Next, (int Latitude, int Longitude) Result)> Variances => [
- ((10, 10), (-20, -20), (-30, -30)),
- ((-10, -10), (20, 20), (30, 30)),
- ((0, 10), (10, -10), (10, -20)),
- ((0, -10), (10, 10), (10, 20)),
- ((10, 0), (10, -10), (0, -10)),
- ((-10, 0), (10, 10), (20, 10)),
- ((10, -10), (-10, 10), (-20, 20)),
- ((-10, 10), (10, 10), (20, 0)),
- ((10, 10), (10, 0), (0, -10)),
- ((-10, -10), (-10, 0), (0, 10)),
- ((10, 10), (0, 0), (-10, -10)),
- ((-10, -10), (0, 0), (10, 10)),
- ((10, -10), (0, 0), (-10, 10)),
- ((-10, 10), (0, 0), (10, -10)),
- ((0, 10), (0, 0), (0, -10)),
- ((0, -10), (0, 0), (0, 10)),
- ((10, 0), (0, 0), (-10, 0)),
- ((-10, 0), (0, 0), (10, 0))
- ];
-
- [TestMethod]
- public void Constructor_Sets_Defaults() {
- // Arrange & Act
- CoordinateVariance variance = new();
- // Assert
- Assert.AreEqual(0, variance.Latitude);
- Assert.AreEqual(0, variance.Longitude);
- }
-
- [TestMethod]
- [DynamicData(nameof(Coordinates), DynamicDataSourceType.Property)]
- public void Next_Calculates_Correct_Variance_From_Default_Variance(int latitude, int longitude) {
- // Arrange
- CoordinateVariance variance = new();
- var expected = (latitude, longitude);
-
- // Act
- variance.Next(latitude, longitude);
-
- // Assert
- Assert.AreEqual(expected.latitude, variance.Latitude);
- Assert.AreEqual(expected.longitude, variance.Longitude);
- }
-
- [TestMethod]
- [DynamicData(nameof(Variances), DynamicDataSourceType.Property)]
- public void Next_Calculates_Correct_Variance_From_Previous_Variance((int Latitude, int Longitude) initial, (int Latitude, int Longitude) next, (int Latitude, int Longitude) expected) {
- // Arrange
- CoordinateVariance variance = new();
- variance.Next(initial.Latitude, initial.Longitude);
-
- // Act
- variance.Next(next.Latitude, next.Longitude);
-
- // Assert
- Assert.AreEqual(expected.Latitude, variance.Latitude);
- Assert.AreEqual(expected.Longitude, variance.Longitude);
- }
-
- [TestMethod]
- [DynamicData(nameof(Coordinates), DynamicDataSourceType.Property)]
- public void ToString_Returns_Value_Containing_Variance(int latitude, int longitude) {
- // Arrange
- CoordinateVariance variance = new();
- variance.Next(latitude, longitude);
-
- // Act
- string result = variance.ToString();
-
- // Assert
- Assert.Contains($"Latitude: {latitude}", result);
- Assert.Contains($"Longitude: {longitude}", result);
- }
-}
diff --git a/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/ExceptionGuardTests.cs b/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/ExceptionGuardTests.cs
new file mode 100644
index 00000000..ad80846e
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/ExceptionGuardTests.cs
@@ -0,0 +1,701 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Internal.Diagnostics;
+
+using PolylineAlgorithm.Diagnostics;
+using PolylineAlgorithm.Internal.Diagnostics;
+using PolylineAlgorithm.Tests.Properties;
+using System;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class ExceptionGuardTests {
+ ///
+ /// Tests that ThrowNotFiniteNumber throws ArgumentOutOfRangeException with correct parameter name.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowNotFiniteNumber_WithParamName_ThrowsArgumentOutOfRangeException() {
+ // Arrange
+ string paramName = "value";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowNotFiniteNumber(paramName);
+ Assert.Fail("Expected ArgumentOutOfRangeException was not thrown.");
+ } catch (ArgumentOutOfRangeException ex) {
+ Assert.AreEqual(paramName, ex.ParamName);
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowArgumentNull throws ArgumentNullException with correct parameter name.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowArgumentNull_WithParamName_ThrowsArgumentNullException() {
+ // Arrange
+ string paramName = "input";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowArgumentNull(paramName);
+ Assert.Fail("Expected ArgumentNullException was not thrown.");
+ } catch (ArgumentNullException ex) {
+ Assert.AreEqual(paramName, ex.ParamName);
+ }
+ }
+
+ ///
+ /// Tests that ThrowBufferOverflow throws OverflowException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowBufferOverflow_WithMessage_ThrowsOverflowException() {
+ // Arrange
+ string message = "Buffer overflow occurred.";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowBufferOverflow(message);
+ Assert.Fail("Expected OverflowException was not thrown.");
+ } catch (OverflowException ex) {
+ Assert.AreEqual(message, ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowCoordinateValueOutOfRange throws ArgumentOutOfRangeException with correct parameter name.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowCoordinateValueOutOfRange_WithParameters_ThrowsArgumentOutOfRangeException() {
+ // Arrange
+ double value = 100.0;
+ double min = -90.0;
+ double max = 90.0;
+ string paramName = "latitude";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowCoordinateValueOutOfRange(value, min, max, paramName);
+ Assert.Fail("Expected ArgumentOutOfRangeException was not thrown.");
+ } catch (ArgumentOutOfRangeException ex) {
+ Assert.AreEqual(paramName, ex.ParamName);
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that StackAllocLimitMustBeEqualOrGreaterThan throws ArgumentOutOfRangeException with correct parameter name.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void StackAllocLimitMustBeEqualOrGreaterThan_WithParameters_ThrowsArgumentOutOfRangeException() {
+ // Arrange
+ int minValue = 10;
+ string paramName = "stackAllocLimit";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.StackAllocLimitMustBeEqualOrGreaterThan(minValue, paramName);
+ Assert.Fail("Expected ArgumentOutOfRangeException was not thrown.");
+ } catch (ArgumentOutOfRangeException ex) {
+ Assert.AreEqual(paramName, ex.ParamName);
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrwoArgumentCannotBeEmptyEnumerationMessage throws ArgumentException with correct parameter name.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrwoArgumentCannotBeEmptyEnumerationMessage_WithParamName_ThrowsArgumentException() {
+ // Arrange
+ string paramName = "collection";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrwoArgumentCannotBeEmptyEnumerationMessage(paramName);
+ Assert.Fail("Expected ArgumentException was not thrown.");
+ } catch (ArgumentException ex) {
+ Assert.AreEqual(paramName, ex.ParamName);
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowCouldNotWriteEncodedValueToBuffer throws InvalidOperationException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowCouldNotWriteEncodedValueToBuffer_ThrowsInvalidOperationException() {
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer();
+ Assert.Fail("Expected InvalidOperationException was not thrown.");
+ } catch (InvalidOperationException ex) {
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength throws ArgumentException with correct parameter name.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength_WithParameters_ThrowsArgumentException() {
+ // Arrange
+ int destinationLength = 5;
+ int polylineLength = 10;
+ string paramName = "destination";
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(destinationLength, polylineLength, paramName);
+ Assert.Fail("Expected ArgumentException was not thrown.");
+ } catch (ArgumentException ex) {
+ Assert.AreEqual(paramName, ex.ParamName);
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowInvalidPolylineLength throws InvalidPolylineException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowInvalidPolylineLength_WithParameters_ThrowsInvalidPolylineException() {
+ // Arrange
+ int length = 5;
+ int min = 10;
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowInvalidPolylineLength(length, min);
+ Assert.Fail("Expected InvalidPolylineException was not thrown.");
+ } catch (InvalidPolylineException ex) {
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowInvalidPolylineCharacter throws InvalidPolylineException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowInvalidPolylineCharacter_WithParameters_ThrowsInvalidPolylineException() {
+ // Arrange
+ char character = '!';
+ int position = 15;
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowInvalidPolylineCharacter(character, position);
+ Assert.Fail("Expected InvalidPolylineException was not thrown.");
+ } catch (InvalidPolylineException ex) {
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowPolylineBlockTooLong throws InvalidPolylineException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowPolylineBlockTooLong_WithPosition_ThrowsInvalidPolylineException() {
+ // Arrange
+ int position = 42;
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowPolylineBlockTooLong(position);
+ Assert.Fail("Expected InvalidPolylineException was not thrown.");
+ } catch (InvalidPolylineException ex) {
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowInvalidPolylineFormat throws InvalidPolylineException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowInvalidPolylineFormat_WithPosition_ThrowsInvalidPolylineException() {
+ // Arrange
+ long position = 100L;
+
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowInvalidPolylineFormat(position);
+ Assert.Fail("Expected InvalidPolylineException was not thrown.");
+ } catch (InvalidPolylineException ex) {
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that ThrowInvalidPolylineBlockTerminator throws InvalidPolylineException with correct message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void ThrowInvalidPolylineBlockTerminator_ThrowsInvalidPolylineException() {
+ // Act & Assert
+ try {
+ ExceptionGuard.ThrowInvalidPolylineBlockTerminator();
+ Assert.Fail("Expected InvalidPolylineException was not thrown.");
+ } catch (InvalidPolylineException ex) {
+ Assert.IsNotNull(ex.Message);
+ }
+ }
+
+ ///
+ /// Tests that FormatStackAllocLimitMustBeEqualOrGreaterThan returns formatted message with specified value.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatStackAllocLimitMustBeEqualOrGreaterThan_WithMinValue_ReturnsFormattedMessage() {
+ // Arrange
+ int minValue = 10;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatStackAllocLimitMustBeEqualOrGreaterThan(minValue);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("10", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatPolylineCannotBeShorterThan returns formatted message with specified values.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatPolylineCannotBeShorterThan_WithLengthAndMinLength_ReturnsFormattedMessage() {
+ // Arrange
+ int length = 5;
+ int minLength = 10;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatPolylineCannotBeShorterThan(length, minLength);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('5'));
+ Assert.IsTrue(result.Contains("10", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatMalformedPolyline returns formatted message with position.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatMalformedPolyline_WithPosition_ReturnsFormattedMessage() {
+ // Arrange
+ long position = 42L;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatMalformedPolyline(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("42", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatMalformedPolyline with zero position returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatMalformedPolyline_WithZeroPosition_ReturnsFormattedMessage() {
+ // Arrange
+ long position = 0L;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatMalformedPolyline(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('0'));
+ }
+
+ ///
+ /// Tests that FormatMalformedPolyline with negative position returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatMalformedPolyline_WithNegativePosition_ReturnsFormattedMessage() {
+ // Arrange
+ long position = -10L;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatMalformedPolyline(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("-10", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatMalformedPolyline with large position returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatMalformedPolyline_WithLargePosition_ReturnsFormattedMessage() {
+ // Arrange
+ long position = long.MaxValue;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatMalformedPolyline(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains(long.MaxValue.ToString(System.Globalization.CultureInfo.InvariantCulture), StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatCoordinateValueMustBeBetween returns formatted message with all parameters.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatCoordinateValueMustBeBetween_WithParameters_ReturnsFormattedMessage() {
+ // Arrange
+ string name = "latitude";
+ double min = -90.0;
+ double max = 90.0;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatCoordinateValueMustBeBetween(name, min, max);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("latitude", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("-90", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("90", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatCoordinateValueMustBeBetween with positive values returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatCoordinateValueMustBeBetween_WithPositiveValues_ReturnsFormattedMessage() {
+ // Arrange
+ string name = "longitude";
+ double min = 0.0;
+ double max = 180.0;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatCoordinateValueMustBeBetween(name, min, max);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("longitude", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains('0'));
+ Assert.IsTrue(result.Contains("180", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatCoordinateValueMustBeBetween with fractional values returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatCoordinateValueMustBeBetween_WithFractionalValues_ReturnsFormattedMessage() {
+ // Arrange
+ string name = "value";
+ double min = 1.5;
+ double max = 10.75;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatCoordinateValueMustBeBetween(name, min, max);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("value", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatPolylineBlockTooLong returns formatted message with position.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatPolylineBlockTooLong_WithPosition_ReturnsFormattedMessage() {
+ // Arrange
+ int position = 15;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatPolylineBlockTooLong(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("15", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatPolylineBlockTooLong with zero position returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatPolylineBlockTooLong_WithZeroPosition_ReturnsFormattedMessage() {
+ // Arrange
+ int position = 0;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatPolylineBlockTooLong(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('0'));
+ }
+
+ ///
+ /// Tests that FormatPolylineBlockTooLong with large position returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatPolylineBlockTooLong_WithLargePosition_ReturnsFormattedMessage() {
+ // Arrange
+ int position = int.MaxValue;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatPolylineBlockTooLong(position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains(int.MaxValue.ToString(System.Globalization.CultureInfo.InvariantCulture), StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatInvalidPolylineCharacter returns formatted message with character and position.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatInvalidPolylineCharacter_WithCharacterAndPosition_ReturnsFormattedMessage() {
+ // Arrange
+ char character = '!';
+ int position = 10;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatInvalidPolylineCharacter(character, position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('!'));
+ Assert.IsTrue(result.Contains("10", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatInvalidPolylineCharacter with letter character returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatInvalidPolylineCharacter_WithLetterCharacter_ReturnsFormattedMessage() {
+ // Arrange
+ char character = 'Z';
+ int position = 5;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatInvalidPolylineCharacter(character, position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('Z'));
+ Assert.IsTrue(result.Contains('5'));
+ }
+
+ ///
+ /// Tests that FormatInvalidPolylineCharacter with special character returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatInvalidPolylineCharacter_WithSpecialCharacter_ReturnsFormattedMessage() {
+ // Arrange
+ char character = '@';
+ int position = 0;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatInvalidPolylineCharacter(character, position);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('@'));
+ Assert.IsTrue(result.Contains('0'));
+ }
+
+ ///
+ /// Tests that FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength_WithLengths_ReturnsFormattedMessage() {
+ // Arrange
+ int destinationLength = 5;
+ int polylineLength = 10;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(destinationLength, polylineLength);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('5'));
+ Assert.IsTrue(result.Contains("10", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength with zero destination length returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength_WithZeroDestinationLength_ReturnsFormattedMessage() {
+ // Arrange
+ int destinationLength = 0;
+ int polylineLength = 100;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(destinationLength, polylineLength);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('0'));
+ Assert.IsTrue(result.Contains("100", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength with large values returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength_WithLargeValues_ReturnsFormattedMessage() {
+ // Arrange
+ int destinationLength = 1000;
+ int polylineLength = 2000;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatDestinationArrayLengthMustBeEqualOrGreaterThanPolylineLength(destinationLength, polylineLength);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("1000", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains("2000", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatInvalidPolylineLength returns formatted message with length and min values.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatInvalidPolylineLength_WithLengthAndMin_ReturnsFormattedMessage() {
+ // Arrange
+ int length = 5;
+ int min = 10;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatInvalidPolylineLength(length, min);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('5'));
+ Assert.IsTrue(result.Contains("10", StringComparison.Ordinal));
+ }
+
+ ///
+ /// Tests that FormatInvalidPolylineLength with zero length returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatInvalidPolylineLength_WithZeroLength_ReturnsFormattedMessage() {
+ // Arrange
+ int length = 0;
+ int min = 1;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatInvalidPolylineLength(length, min);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains('0'));
+ Assert.IsTrue(result.Contains('1'));
+ }
+
+ ///
+ /// Tests that FormatInvalidPolylineLength with negative values returns formatted message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void FormatInvalidPolylineLength_WithNegativeValues_ReturnsFormattedMessage() {
+ // Arrange
+ int length = -5;
+ int min = 0;
+
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.FormatInvalidPolylineLength(length, min);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Contains("-5", StringComparison.Ordinal));
+ Assert.IsTrue(result.Contains('0'));
+ }
+
+ ///
+ /// Tests that GetArgumentValueMustBeFiniteNumber returns non-null message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetArgumentValueMustBeFiniteNumber_ReturnsNonNullMessage() {
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.GetArgumentValueMustBeFiniteNumber();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(result));
+ }
+
+ ///
+ /// Tests that GetCouldNotWriteEncodedValueToTheBuffer returns non-null message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetCouldNotWriteEncodedValueToTheBuffer_ReturnsNonNullMessage() {
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.GetCouldNotWriteEncodedValueToTheBuffer();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(result));
+ }
+
+ ///
+ /// Tests that GetArgumentCannotBeEmpty returns non-null message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetArgumentCannotBeEmpty_ReturnsNonNullMessage() {
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.GetArgumentCannotBeEmpty();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(result));
+ }
+
+ ///
+ /// Tests that GetInvalidPolylineBlockTerminator returns non-null message.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetInvalidPolylineBlockTerminator_ReturnsNonNullMessage() {
+ // Act
+ string result = ExceptionGuard.ExceptionMessage.GetInvalidPolylineBlockTerminator();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(result));
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/LogDebugExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/LogDebugExtensionsTests.cs
new file mode 100644
index 00000000..a51c1d70
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/LogDebugExtensionsTests.cs
@@ -0,0 +1,214 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Internal.Diagnostics;
+
+using Microsoft.Extensions.Logging;
+using PolylineAlgorithm.Internal.Diagnostics;
+using PolylineAlgorithm.Tests.Properties;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class LogDebugExtensionsTests
+{
+ private sealed class TestLogger : ILogger
+ {
+ public List<(LogLevel Level, EventId EventId, string Message, Exception? Exception)> Logs { get; } = new();
+
+ public IDisposable BeginScope(TState state) => NullScope.Instance;
+
+ public bool IsEnabled(LogLevel logLevel) => true;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
+ {
+ Logs.Add((logLevel, eventId, formatter(state, exception), exception));
+ }
+
+ private sealed class NullScope : IDisposable
+ {
+ public static NullScope Instance { get; } = new();
+ public void Dispose() { }
+ }
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationStartedDebug_WithOperationName_LogsStartedMessage()
+ {
+ var logger = new TestLogger();
+ string operationName = "TestOperation";
+
+ logger.LogOperationStartedDebug(operationName);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, $"Operation {operationName} has started.");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationFailedDebug_WithOperationName_LogsFailedMessage()
+ {
+ var logger = new TestLogger();
+ string operationName = "TestOperation";
+
+ logger.LogOperationFailedDebug(operationName);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, $"Operation {operationName} has failed.");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationFinishedDebug_WithOperationName_LogsFinishedMessage()
+ {
+ var logger = new TestLogger();
+ string operationName = "TestOperation";
+
+ logger.LogOperationFinishedDebug(operationName);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, $"Operation {operationName} has finished.");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogDecodedCoordinateDebug_WithCoordinatesAndPosition_LogsDecodedCoordinateMessage()
+ {
+ var logger = new TestLogger();
+ double latitude = 38.5;
+ double longitude = -120.2;
+ int position = 42;
+
+ logger.LogDecodedCoordinateDebug(latitude, longitude, position);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, $"Decoded coordinate: (Latitude: {latitude}, Longitude: {longitude}) at position {position}.");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationStartedDebug_WithNullOperationName_LogsMessage()
+ {
+ var logger = new TestLogger();
+ string? operationName = null;
+
+ logger.LogOperationStartedDebug(operationName!);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Operation");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationFailedDebug_WithNullOperationName_LogsMessage()
+ {
+ var logger = new TestLogger();
+ string? operationName = null;
+
+ logger.LogOperationFailedDebug(operationName!);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Operation");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationFinishedDebug_WithNullOperationName_LogsMessage()
+ {
+ var logger = new TestLogger();
+ string? operationName = null;
+
+ logger.LogOperationFinishedDebug(operationName!);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Operation");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogDecodedCoordinateDebug_WithZeroCoordinates_LogsMessage()
+ {
+ var logger = new TestLogger();
+ double latitude = 0.0;
+ double longitude = 0.0;
+ int position = 0;
+
+ logger.LogDecodedCoordinateDebug(latitude, longitude, position);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Decoded coordinate");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogDecodedCoordinateDebug_WithNegativeCoordinates_LogsMessage()
+ {
+ var logger = new TestLogger();
+ double latitude = -90.0;
+ double longitude = -180.0;
+ int position = 100;
+
+ logger.LogDecodedCoordinateDebug(latitude, longitude, position);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, $"Latitude: {latitude}, Longitude: {longitude}");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationStartedDebug_WithEmptyOperationName_LogsMessage()
+ {
+ var logger = new TestLogger();
+ string operationName = string.Empty;
+
+ logger.LogOperationStartedDebug(operationName);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Operation");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationFailedDebug_WithEmptyOperationName_LogsMessage()
+ {
+ var logger = new TestLogger();
+ string operationName = string.Empty;
+
+ logger.LogOperationFailedDebug(operationName);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Operation");
+ }
+
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void LogOperationFinishedDebug_WithEmptyOperationName_LogsMessage()
+ {
+ var logger = new TestLogger();
+ string operationName = string.Empty;
+
+ logger.LogOperationFinishedDebug(operationName);
+
+ var log = logger.Logs.Single();
+ Assert.AreEqual(LogLevel.Debug, log.Level);
+ StringAssert.Contains(log.Message, "Operation");
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/LogWarningExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/LogWarningExtensionsTests.cs
new file mode 100644
index 00000000..ada0eafa
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Internal/Diagnostics/LogWarningExtensionsTests.cs
@@ -0,0 +1,102 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Internal.Diagnostics;
+
+using Microsoft.Extensions.Logging;
+using PolylineAlgorithm.Internal.Diagnostics;
+using PolylineAlgorithm.Tests.Properties;
+using System;
+using System.Collections.Generic;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class LogWarningExtensionsTests
+{
+ private sealed class TestLogger : ILogger
+ {
+ public List<(LogLevel Level, EventId EventId, string Message, Exception? Exception)> Logs { get; } = new();
+
+ public IDisposable BeginScope(TState state) => NullScope.Instance;
+ public bool IsEnabled(LogLevel logLevel) => true;
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
+ {
+ Logs.Add((logLevel, eventId, formatter(state, exception), exception));
+ }
+
+ private class NullScope : IDisposable
+ {
+ public static NullScope Instance { get; } = new();
+ public void Dispose() { }
+ }
+ }
+
+ [TestMethod]
+ public void LogNullArgumentWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogNullArgumentWarning("foo");
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Argument foo is null.")));
+ }
+
+ [TestMethod]
+ public void LogEmptyArgumentWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogEmptyArgumentWarning("bar");
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Argument bar is empty.")));
+ }
+
+ [TestMethod]
+ public void LogInternalBufferOverflowWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogInternalBufferOverflowWarning(1, 2, 3);
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Internal buffer has size of 2. At position 1 is required additional 3 space.")));
+ }
+
+ [TestMethod]
+ public void LogCannotWriteValueToBufferWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogCannotWriteValueToBufferWarning(4, 5);
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Cannot write to internal buffer at position 4. Current coordinate is at index 5.")));
+ }
+
+ [TestMethod]
+ public void LogPolylineCannotBeShorterThanWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogPolylineCannotBeShorterThanWarning(6, 7);
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Polyline is too short. Minimal length is 7. Actual length is 6.")));
+ }
+
+ [TestMethod]
+ public void LogRequestedBufferSizeExceedsMaxBufferLengthWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogRequestedBufferSizeExceedsMaxBufferLengthWarning(8, 9);
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Requested buffer size of 8 exceeds maximum allowed buffer length of 9.")));
+ }
+
+ [TestMethod]
+ public void LogInvalidPolylineWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ logger.LogInvalidPolylineWarning(10);
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Polyline is invalid or malformed at position 10.")));
+ }
+
+ [TestMethod]
+ public void LogInvalidPolylineFormatWarning_LogsExpectedMessage()
+ {
+ var logger = new TestLogger();
+ var ex = new Exception("fail");
+ logger.LogInvalidPolylineFormatWarning(ex);
+ Assert.IsTrue(logger.Logs.Exists(l => l.Message.Contains("Polyline is invalid or malformed.") && l.Exception == ex));
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/Internal/LoggingTest.cs b/tests/PolylineAlgorithm.Tests/Internal/LoggingTest.cs
deleted file mode 100644
index ba09fa8d..00000000
--- a/tests/PolylineAlgorithm.Tests/Internal/LoggingTest.cs
+++ /dev/null
@@ -1,224 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests.Internal;
-
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Testing;
-using PolylineAlgorithm.Internal.Logging;
-using PolylineAlgorithm.Tests.Fakes;
-
-[TestClass]
-public class LoggingTest {
- private static readonly FakeLoggerProvider _loggerProvider = new();
- private static readonly ILoggerFactory _loggerFactory = new FakeLoggerFactory(_loggerProvider);
-
- [TestMethod]
- [DataRow(null)]
- [DataRow("")]
- [DataRow(" ")]
- [DataRow("operationName")]
- public void ILogger_LogOperationStartedInfo_Ok(string value) {
- // Arrange
- string operationName = value;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogOperationStartedInfo(operationName);
-
- // Assert
- Assert.AreEqual(new EventId(201, nameof(LogInfoExtensions.LogOperationStartedInfo)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Information, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Operation {value ?? "(null)"} has started.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- [DataRow(null)]
- [DataRow("")]
- [DataRow(" ")]
- [DataRow("operationName")]
- public void ILogger_LogOperationFailedInfo_Ok(string value) {
- // Arrange
- string operationName = value;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogOperationFailedInfo(operationName);
-
- // Assert
- Assert.AreEqual(new EventId(202, nameof(LogInfoExtensions.LogOperationFailedInfo)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Information, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Operation {value ?? "(null)"} has failed.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- [DataRow(null)]
- [DataRow("")]
- [DataRow(" ")]
- [DataRow("operationName")]
- public void ILogger_LogOperationFinishedInfo_Ok(string value) {
- // Arrange
- string operationName = value;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogOperationFinishedInfo(operationName);
-
- // Assert
- Assert.AreEqual(new EventId(203, nameof(LogInfoExtensions.LogOperationFinishedInfo)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Information, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Operation {value ?? "(null)"} has finished.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- [DataRow(null)]
- [DataRow("")]
- [DataRow(" ")]
- [DataRow("argumentName")]
- public void ILogger_LogNullArgumentWarning_Ok(string value) {
- // Arrange
- string argumentName = value;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogNullArgumentWarning(argumentName);
-
- // Assert
- Assert.AreEqual(new EventId(301, nameof(LogWarningExtensions.LogNullArgumentWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual($"Argument {value ?? "(null)"} is null.", _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- [DataRow(null)]
- [DataRow("")]
- [DataRow(" ")]
- [DataRow("argumentName")]
- public void ILogger_LogEmptyArgumentWarning_Ok(string value) {
- // Arrange
- string argumentName = value;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogEmptyArgumentWarning(argumentName);
-
- // Assert
- Assert.AreEqual(new EventId(302, nameof(LogWarningExtensions.LogEmptyArgumentWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual($"Argument {value ?? "(null)"} is empty.", _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- public void ILogger_LogInternalBufferOverflowWarning_Ok() {
- // Arrange
- int position = 5;
- int bufferLength = 10;
- int requiredSpace = 15;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogInternalBufferOverflowWarning(position, bufferLength, requiredSpace);
-
- // Assert
- Assert.AreEqual(new EventId(303, nameof(LogWarningExtensions.LogInternalBufferOverflowWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Internal buffer has size of {bufferLength}. At position {position} is required additional {requiredSpace} space.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- public void ILogger_LogCannotWriteValueToBufferWarning_Ok() {
- // Arrange
- int position = 5;
- int index = 1;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogCannotWriteValueToBufferWarning(position, index);
-
- // Assert
- Assert.AreEqual(new EventId(304, nameof(LogWarningExtensions.LogCannotWriteValueToBufferWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Cannot write to internal buffer at position {position}. Current coordinate is at index {index}.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- [DataRow(null)]
- [DataRow("")]
- [DataRow(" ")]
- [DataRow("argumentName")]
- public void ILogger_LogPolylineCannotBeShorterThanWarning_Ok(string value) {
- // Arrange
- string argumentName = value;
- int actualLength = 10;
- int minimumLength = 5;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogPolylineCannotBeShorterThanWarning(argumentName, actualLength, minimumLength);
-
- // Assert
- Assert.AreEqual(new EventId(305, nameof(LogWarningExtensions.LogPolylineCannotBeShorterThanWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Argument {value} is too short. Minimal length is {minimumLength}. Actual length is {actualLength}.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
-
- [TestMethod]
- public void ILogger_LogRequestedBufferSizeExceedsMaxBufferLengthWarning_Ok() {
- // Arrange
- int requestedBufferLength = 5;
- int maxBufferLength = 10;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogRequestedBufferSizeExceedsMaxBufferLengthWarning(requestedBufferLength, maxBufferLength);
-
- // Assert
- Assert.AreEqual(new EventId(306, nameof(LogWarningExtensions.LogRequestedBufferSizeExceedsMaxBufferLengthWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Requested buffer size of {requestedBufferLength} exceeds maximum allowed buffer length of {maxBufferLength}.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-
- [TestMethod]
- public void ILogger_LogInvalidPolylineWarning_Ok() {
- // Arrange
- int position = 5;
-
- // Act
- _loggerFactory
- .CreateLogger()
- .LogInvalidPolylineWarning(position);
-
- // Assert
- Assert.AreEqual(new EventId(307, nameof(LogWarningExtensions.LogInvalidPolylineWarning)), _loggerProvider.Collector.LatestRecord.Id);
- Assert.AreEqual(LogLevel.Warning, _loggerProvider.Collector.LatestRecord.Level);
- Assert.AreEqual(
- $"Polyline is invalid or malformed at position {position}.",
- _loggerProvider.Collector.LatestRecord.Message);
- }
-}
diff --git a/tests/PolylineAlgorithm.Tests/Internal/Pow10Tests.cs b/tests/PolylineAlgorithm.Tests/Internal/Pow10Tests.cs
new file mode 100644
index 00000000..b6f190f0
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/Internal/Pow10Tests.cs
@@ -0,0 +1,175 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests.Internal;
+
+using PolylineAlgorithm.Internal;
+using PolylineAlgorithm.Tests.Properties;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Pow10Tests {
+ ///
+ /// Tests that GetFactor with precision 0 returns 1.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Zero_Returns_One() {
+ // Act
+ uint result = Pow10.GetFactor(0);
+
+ // Assert
+ Assert.AreEqual(1u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 1 returns 10.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_One_Returns_Ten() {
+ // Act
+ uint result = Pow10.GetFactor(1);
+
+ // Assert
+ Assert.AreEqual(10u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 2 returns 100.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Two_Returns_One_Hundred() {
+ // Act
+ uint result = Pow10.GetFactor(2);
+
+ // Assert
+ Assert.AreEqual(100u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 3 returns 1000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Three_Returns_One_Thousand() {
+ // Act
+ uint result = Pow10.GetFactor(3);
+
+ // Assert
+ Assert.AreEqual(1000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 4 returns 10000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Four_Returns_Ten_Thousand() {
+ // Act
+ uint result = Pow10.GetFactor(4);
+
+ // Assert
+ Assert.AreEqual(10000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 5 returns 100000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Five_Returns_One_Hundred_Thousand() {
+ // Act
+ uint result = Pow10.GetFactor(5);
+
+ // Assert
+ Assert.AreEqual(100000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 6 returns 1000000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Six_Returns_One_Million() {
+ // Act
+ uint result = Pow10.GetFactor(6);
+
+ // Assert
+ Assert.AreEqual(1000000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 7 returns 10000000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Seven_Returns_Ten_Million() {
+ // Act
+ uint result = Pow10.GetFactor(7);
+
+ // Assert
+ Assert.AreEqual(10000000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 8 returns 100000000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Eight_Returns_One_Hundred_Million() {
+ // Act
+ uint result = Pow10.GetFactor(8);
+
+ // Assert
+ Assert.AreEqual(100000000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision 9 returns 1000000000.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Nine_Returns_One_Billion() {
+ // Act
+ uint result = Pow10.GetFactor(9);
+
+ // Assert
+ Assert.AreEqual(1000000000u, result);
+ }
+
+ ///
+ /// Tests that GetFactor with precision causing overflow throws OverflowException.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Precision_Causing_Overflow_Throws_OverflowException() {
+ // Act & Assert
+ Assert.ThrowsExactly(() => Pow10.GetFactor(15));
+ }
+
+ ///
+ /// Tests that GetFactor with large precision causing overflow throws OverflowException.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Large_Precision_Causing_Overflow_Throws_OverflowException() {
+ // Act & Assert
+ Assert.ThrowsExactly(() => Pow10.GetFactor(20));
+ }
+
+ ///
+ /// Tests that GetFactor with maximum uint precision throws OverflowException.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void GetFactor_With_Maximum_Uint_Precision_Throws_OverflowException() {
+ // Act & Assert
+ Assert.ThrowsExactly(() => Pow10.GetFactor(uint.MaxValue));
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/InvalidPolylineExceptionTest.cs b/tests/PolylineAlgorithm.Tests/InvalidPolylineExceptionTest.cs
deleted file mode 100644
index 532b27b8..00000000
--- a/tests/PolylineAlgorithm.Tests/InvalidPolylineExceptionTest.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using PolylineAlgorithm;
-
-///
-/// Defines tests for the type.
-///
-[TestClass]
-public class InvalidPolylineExceptionTest {
- ///
- /// Tests the method with an invalid coordinate parameter, expecting an .
- ///
- [TestMethod]
- public void Throw_Method_Invalid_Coordinate_Parameter_PolylineMalformedException_Throw() {
- // Arrange
- var position = Random.Shared.Next();
- static void ThrowAt(int position) => InvalidPolylineException.Throw(position);
-
- // Act
- var exception = Assert.ThrowsExactly(() => ThrowAt(position));
-
- // Assert
- Assert.IsFalse(string.IsNullOrWhiteSpace(exception.Message));
- Assert.Contains(position.ToString(), exception.Message);
- }
-}
\ No newline at end of file
diff --git a/tests/PolylineAlgorithm.Tests/PolylineAlgorithm.Tests.csproj b/tests/PolylineAlgorithm.Tests/PolylineAlgorithm.Tests.csproj
index 974f5cd5..72fdded9 100644
--- a/tests/PolylineAlgorithm.Tests/PolylineAlgorithm.Tests.csproj
+++ b/tests/PolylineAlgorithm.Tests/PolylineAlgorithm.Tests.csproj
@@ -1,11 +1,7 @@
- net10.0
- 13.0
- enable
- enable
- true
+ net8.0;net9.0;net10.0;
@@ -19,18 +15,14 @@
false
-
- All
- latest
- true
- false
-
-
+
+
+
+
-
diff --git a/tests/PolylineAlgorithm.Tests/PolylineDecoderExtensionsTest.cs b/tests/PolylineAlgorithm.Tests/PolylineDecoderExtensionsTest.cs
deleted file mode 100644
index 70030036..00000000
--- a/tests/PolylineAlgorithm.Tests/PolylineDecoderExtensionsTest.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using PolylineAlgorithm.Extensions;
-using PolylineAlgorithm.Utility;
-using System.Collections.Generic;
-using System.Linq;
-
-[TestClass]
-public class PolylineDecoderExtensionsTest {
- private readonly PolylineDecoder _decoder = new();
-
- public static IEnumerable CoordinateCount => [[1], [10], [100], [1_000]];
-
- [TestMethod]
- public void Decode_Null_Decoder_Null_String_Throws_ArgumentNullException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => PolylineDecoderExtensions.Decode(null!, string.Empty).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.AreEqual("decoder", exception.ParamName);
- Assert.IsTrue(exception.Message.Contains("Value cannot be null.", StringComparison.Ordinal));
- }
-
- [TestMethod]
- public void Decode_Null_Decoder_Null_CharArray_Throws_ArgumentNullException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => PolylineDecoderExtensions.Decode(null!, []).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.AreEqual("decoder", exception.ParamName);
- Assert.IsTrue(exception.Message.Contains("Value cannot be null.", StringComparison.Ordinal));
- }
-
- [TestMethod]
- public void Decode_Null_Decoder_Empty_Memory_Throws_ArgumentNullException() {
- // Arrange
-#pragma warning disable IDE0305 // Simplify collection initialization
- static void Decode() => PolylineDecoderExtensions.Decode(null!, Memory.Empty).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Act
- var exception = Assert.ThrowsExactly(Decode);
-
- // Assert
- Assert.AreEqual("decoder", exception.ParamName);
- Assert.IsTrue(exception.Message.Contains("Value cannot be null.", StringComparison.Ordinal));
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount), DynamicDataSourceType.Property)]
- public void Decode_String_Returns_Expected_Coordinates(int count) {
- // Arrange
- var polyline = RandomValueProvider.GetPolyline(count);
- var expected = RandomValueProvider.GetCoordinates(count)
- .Select(c => new Coordinate(c.Latitude, c.Longitude))
- .ToList();
-
- // Act
- var result = PolylineDecoderExtensions.Decode(_decoder, polyline).ToList();
-
- // Assert
- CollectionAssert.AreEqual(expected, result);
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount), DynamicDataSourceType.Property)]
- public void Decode_CharArray_Returns_Expected_Coordinates(int count) {
- // Arrange
- var polyline = RandomValueProvider.GetPolyline(count).ToCharArray();
- var expected = RandomValueProvider.GetCoordinates(count)
- .Select(c => new Coordinate(c.Latitude, c.Longitude))
- .ToList();
-
- // Act
- var result = PolylineDecoderExtensions.Decode(_decoder, polyline).ToList();
-
- // Assert
- CollectionAssert.AreEqual(expected, result);
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount), DynamicDataSourceType.Property)]
- public void Decode_Memory_Returns_Expected_Coordinates(int count) {
- // Arrange
- var polyline = RandomValueProvider.GetPolyline(count).AsMemory();
- var expected = RandomValueProvider.GetCoordinates(count)
- .Select(c => new Coordinate(c.Latitude, c.Longitude))
- .ToList();
-
- // Act
- var result = PolylineDecoderExtensions.Decode(_decoder, polyline).ToList();
-
- // Assert
- CollectionAssert.AreEqual(expected, result);
- }
-
-}
diff --git a/tests/PolylineAlgorithm.Tests/PolylineDecoderTest.cs b/tests/PolylineAlgorithm.Tests/PolylineDecoderTest.cs
deleted file mode 100644
index 698d99d1..00000000
--- a/tests/PolylineAlgorithm.Tests/PolylineDecoderTest.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using PolylineAlgorithm;
-using PolylineAlgorithm.Utility;
-
-///
-/// Defines tests for the type.
-///
-[TestClass]
-public class PolylineDecoderTest {
- public static IEnumerable CoordinateCount => [[1], [10], [100], [1_000]];
-
- ///
- /// The instance of the used for testing.
- ///
- public PolylineDecoder Decoder = new();
-
- ///
- /// Tests the method with an empty input, expecting an .
- ///
- [TestMethod]
- public void Decode_Default_Polyline_Throws_ArgumentException() {
- // Arrange
- Polyline empty = new();
-
- // Act
-#pragma warning disable IDE0305 // Simplify collection initialization
- void Execute(Polyline value) => Decoder.Decode(value).ToList();
-#pragma warning restore IDE0305 // Simplify collection initialization
-
- // Assert
- Assert.ThrowsExactly(() => Execute(empty));
- }
-
- ///
- /// Tests the method with an invalid input, expecting an .
- ///
- //[TestMethod]
- //public void Decode_Invalid_Input_ThrowsException() {
- // // Arrange
- // Polyline value = Polyline.FromString(StaticValueProvider.GetPolyline());
-
- // // Act
- // void Execute(Polyline value) => Decoder.Decode(value).ToList();
-
- // // Assert
- // var exception = Assert.ThrowsExactly(() => Execute(value));
- //}
-
- ///
- /// Tests the method with a valid input.
- ///
- /// Expected result to equal .
- [TestMethod]
- [DynamicData(nameof(CoordinateCount))]
- public void Random_Value_Decode_Valid_Input_Ok(int count) {
- // Arrange
- IEnumerable expected = RandomValueProvider.GetCoordinates(count).Select(c => new Coordinate(c.Latitude, c.Longitude));
- Polyline value = Polyline.FromString(RandomValueProvider.GetPolyline(count));
-
- // Act
- var result = Decoder.Decode(value);
-
- // Assert
- CollectionAssert.AreEqual(expected.ToArray(), result.ToArray());
- }
-
- [TestMethod]
- public void Static_Value_Decode_Valid_Input_Ok() {
- // Arrange
- IEnumerable expected = StaticValueProvider.Valid.GetCoordinates().Select(c => new Coordinate(c.Latitude, c.Longitude));
- string value = StaticValueProvider.Valid.GetPolyline();
-
- // Act
- var result = Decoder.Decode(Polyline.FromString(value));
-
- // Assert
- CollectionAssert.AreEqual(expected.ToArray(), result.ToArray());
- }
-}
\ No newline at end of file
diff --git a/tests/PolylineAlgorithm.Tests/PolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/PolylineDecoderTests.cs
new file mode 100644
index 00000000..981e8d53
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/PolylineDecoderTests.cs
@@ -0,0 +1,172 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests;
+
+using PolylineAlgorithm.Tests.Properties;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class PolylineDecoderTests {
+ ///
+ /// Tests that default constructor creates decoder with default options.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_DefaultConstructor_CreatesDecoderWithDefaultOptions() {
+ // Arrange & Act
+ PolylineDecoder decoder = new PolylineDecoder();
+
+ // Assert
+ Assert.IsNotNull(decoder);
+ Assert.IsNotNull(decoder.Options);
+ }
+
+ ///
+ /// Tests that default constructor creates decoder with default precision.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_DefaultConstructor_CreatesDecoderWithDefaultPrecision() {
+ // Arrange & Act
+ PolylineDecoder decoder = new PolylineDecoder();
+
+ // Assert
+ Assert.AreEqual(5u, decoder.Options.Precision);
+ }
+
+ ///
+ /// Tests that default constructor creates decoder with default stack alloc limit.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_DefaultConstructor_CreatesDecoderWithDefaultStackAllocLimit() {
+ // Arrange & Act
+ PolylineDecoder decoder = new PolylineDecoder();
+
+ // Assert
+ Assert.AreEqual(512, decoder.Options.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that parameterized constructor creates decoder with specified options.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_WithOptions_CreatesDecoderWithSpecifiedOptions() {
+ // Arrange
+ PolylineEncodingOptions options = new PolylineEncodingOptions {
+ Precision = 6,
+ StackAllocLimit = 1024
+ };
+
+ // Act
+ PolylineDecoder decoder = new PolylineDecoder(options);
+
+ // Assert
+ Assert.IsNotNull(decoder);
+ Assert.AreSame(options, decoder.Options);
+ }
+
+ ///
+ /// Tests that parameterized constructor preserves custom precision.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_WithCustomPrecision_PreservesCustomPrecision() {
+ // Arrange
+ PolylineEncodingOptions options = new PolylineEncodingOptions {
+ Precision = 7
+ };
+
+ // Act
+ PolylineDecoder decoder = new PolylineDecoder(options);
+
+ // Assert
+ Assert.AreEqual(7u, decoder.Options.Precision);
+ }
+
+ ///
+ /// Tests that parameterized constructor preserves custom stack alloc limit.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_WithCustomStackAllocLimit_PreservesCustomStackAllocLimit() {
+ // Arrange
+ PolylineEncodingOptions options = new PolylineEncodingOptions {
+ StackAllocLimit = 2048
+ };
+
+ // Act
+ PolylineDecoder decoder = new PolylineDecoder(options);
+
+ // Assert
+ Assert.AreEqual(2048, decoder.Options.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that parameterized constructor throws ArgumentNullException when options is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_WithNullOptions_ThrowsArgumentNullException() {
+ // Arrange
+ PolylineEncodingOptions? options = null;
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(
+ () => new PolylineDecoder(options!));
+ Assert.AreEqual("options", exception.ParamName);
+ }
+
+ ///
+ /// Tests that decodes a valid polyline to expected coordinates.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_Decode_ReturnsExpectedCoordinates()
+ {
+ // Arrange
+ PolylineDecoder decoder = new PolylineDecoder();
+ Polyline polyline = Polyline.FromString("_p~iF~ps|U_ulLnnqC");
+ Coordinate[] expected =
+ [
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95)
+ ];
+
+ // Act
+ var result = decoder.Decode(polyline).ToArray();
+
+ // Assert
+ CollectionAssert.AreEqual(expected, result);
+ }
+
+ ///
+ /// Tests that decodes a valid polyline to expected coordinates.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineDecoder_Decode_WithCancellationToken_ReturnsExpectedCoordinates()
+ {
+ // Arrange
+ PolylineDecoder decoder = new PolylineDecoder();
+ Polyline polyline = Polyline.FromString("_p~iF~ps|U_ulLnnqC");
+ Coordinate[] expected =
+ [
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95)
+ ];
+ CancellationToken token = CancellationToken.None;
+
+ // Act
+ var result = decoder.Decode(polyline, token).ToArray();
+
+ // Assert
+ CollectionAssert.AreEqual(expected, result);
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncoderExtensionsTest.cs b/tests/PolylineAlgorithm.Tests/PolylineEncoderExtensionsTest.cs
deleted file mode 100644
index 02d6d35a..00000000
--- a/tests/PolylineAlgorithm.Tests/PolylineEncoderExtensionsTest.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using PolylineAlgorithm.Extensions;
-using PolylineAlgorithm.Utility;
-using System.Collections.Generic;
-using System.Linq;
-
-[TestClass]
-public class PolylineEncoderExtensionsTest {
- private readonly PolylineEncoder _encoder = new();
-
- public static IEnumerable CoordinateCount => [[1], [10], [100], [1_000]];
-
- [TestMethod]
- public void Encode_Null_Encoder_Empty_List_Throws_ArgumentNullException() {
- // Arrange
- static void Encode() => PolylineEncoderExtensions.Encode(null!, new List());
-
- // Act
- var exception = Assert.ThrowsExactly(Encode);
-
- // Assert
- Assert.AreEqual("encoder", exception.ParamName);
- Assert.IsTrue(exception.Message.Contains("Value cannot be null.", StringComparison.Ordinal));
- }
-
- [TestMethod]
- public void Encode_Null_Encoder_Null_CharArray_Throws_ArgumentNullException() {
- // Arrange
- static void Encode() => PolylineEncoderExtensions.Encode(null!, []);
-
- // Act
- var exception = Assert.ThrowsExactly(Encode);
-
- // Assert
- Assert.AreEqual("encoder", exception.ParamName);
- Assert.IsTrue(exception.Message.Contains("Value cannot be null.", StringComparison.Ordinal));
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount), DynamicDataSourceType.Property)]
- public void Encode_List_Returns_Expected_Coordinates(int count) {
- // Arrange
- var coordinates = RandomValueProvider.GetCoordinates(count)
- .Select(c => new Coordinate(c.Latitude, c.Longitude))
- .ToList();
- var expected = RandomValueProvider.GetPolyline(count);
-
- // Act
- var result = PolylineEncoderExtensions.Encode(_encoder, coordinates);
-
- // Assert
- Assert.AreEqual(expected, result.ToString());
- }
-
- [TestMethod]
- [DynamicData(nameof(CoordinateCount), DynamicDataSourceType.Property)]
- public void Encode_Array_Returns_Expected_Coordinates(int count) {
- // Arrange
- var coordinates = RandomValueProvider.GetCoordinates(count)
- .Select(c => new Coordinate(c.Latitude, c.Longitude))
- .ToArray();
- var expected = RandomValueProvider.GetPolyline(count);
-
- // Act
- var result = PolylineEncoderExtensions.Encode(_encoder, coordinates);
-
- // Assert
- Assert.AreEqual(expected, result.ToString());
- }
-
-}
diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncoderTest.cs b/tests/PolylineAlgorithm.Tests/PolylineEncoderTest.cs
deleted file mode 100644
index 2d2cd298..00000000
--- a/tests/PolylineAlgorithm.Tests/PolylineEncoderTest.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using PolylineAlgorithm.Utility;
-
-///
-/// Defines tests for the type.
-///
-[TestClass]
-public class PolylineEncoderTest {
- public static IEnumerable CoordinateCount => [[1], [10], [100], [1_000]];
-
- ///
- /// The instance of the used for testing.
- ///
- public PolylineEncoder Encoder = new();
-
- ///
- /// Tests the method with a null input, expecting an .
- ///
- [TestMethod]
- public void Encode_NullInput_ThrowsException() {
- // Arrange
- IEnumerable @null = null!;
-
- // Act
- void EncodeNullCoordinates() {
- Encoder.Encode(@null);
- }
-
- // Assert
- Assert.ThrowsExactly(() => EncodeNullCoordinates());
- }
-
- ///
- /// Tests the method with an empty input, expecting an .
- ///
- [TestMethod]
- public void Encode_EmptyInput_ThrowsException() {
- // Arrange
- IEnumerable empty = [];
-
- // Act
- void EncodeEmptyCoordinates() => Encoder.Encode(empty);
-
- // Assert
- Assert.ThrowsExactly(() => EncodeEmptyCoordinates());
- }
-
- ///
- /// Tests the method with a valid input.
- ///
- /// Expected result is that the encoded polyline matches .
- [TestMethod]
- [DynamicData(nameof(CoordinateCount))]
- public void Encode_RandomValue_ValidInput_Ok(int count) {
- // Arrange
- IEnumerable valid = RandomValueProvider.GetCoordinates(count).Select(c => new Coordinate(c.Latitude, c.Longitude));
- Polyline expected = Polyline.FromString(RandomValueProvider.GetPolyline(count));
-
- // Act
- var result = Encoder.Encode(valid);
-
- // Assert
- Assert.AreEqual(expected.IsEmpty, result.IsEmpty);
- Assert.AreEqual(expected.Length, result.Length);
- Assert.IsTrue(expected.Equals(result));
- }
-
- ///
- /// Tests the method with a valid input.
- ///
- /// Expected result is that the encoded polyline matches .
- [TestMethod]
- public void Encode_StaticValue_ValidInput_Ok() {
- // Arrange
- IEnumerable valid = StaticValueProvider.Valid.GetCoordinates().Select(c => new Coordinate(c.Latitude, c.Longitude));
- Polyline expected = Polyline.FromString(StaticValueProvider.Valid.GetPolyline());
-
- // Act
- var result = Encoder.Encode(valid);
-
- // Assert
- Assert.AreEqual(expected.Length == 0, result.IsEmpty);
- Assert.AreEqual(expected.Length, result.Length);
- Assert.IsTrue(expected.Equals(result));
- }
-
- ///
- /// Tests the round-trip encoding and decoding of coordinates.
- ///
- [TestMethod]
- public void EncodeDecode_RoundTrip_Ok() {
- var coordinates = new List
- {
- new(10, 20),
- new(-10, -20),
- new(0, 0)
- };
-
- var encoder = new PolylineEncoder();
- var decoder = new PolylineDecoder();
-
- var polyline = encoder.Encode(coordinates);
- var decoded = decoder.Decode(polyline).ToList();
-
- Assert.AreEqual(coordinates.Count, decoded.Count);
-
- for (int i = 0; i < coordinates.Count; i++) {
- Assert.AreEqual(coordinates[i].Latitude, decoded[i].Latitude/*, 1e-6*/);
- Assert.AreEqual(coordinates[i].Longitude, decoded[i].Longitude/*, 1e-6*/);
- }
- }
-}
\ No newline at end of file
diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncoderTests.cs
new file mode 100644
index 00000000..4cd40c8e
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/PolylineEncoderTests.cs
@@ -0,0 +1,234 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests;
+
+using PolylineAlgorithm.Tests.Properties;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class PolylineEncoderTests {
+ ///
+ /// Tests that default constructor creates encoder with default options.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_DefaultConstructor_CreatesEncoderWithDefaultOptions() {
+ // Arrange & Act
+ PolylineEncoder encoder = new PolylineEncoder();
+
+ // Assert
+ Assert.IsNotNull(encoder);
+ Assert.IsNotNull(encoder.Options);
+ }
+
+ ///
+ /// Tests that default constructor creates encoder with default precision.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_DefaultConstructor_CreatesEncoderWithDefaultPrecision() {
+ // Arrange & Act
+ PolylineEncoder encoder = new PolylineEncoder();
+
+ // Assert
+ Assert.AreEqual(5u, encoder.Options.Precision);
+ }
+
+ ///
+ /// Tests that default constructor creates encoder with default stack alloc limit.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_DefaultConstructor_CreatesEncoderWithDefaultStackAllocLimit() {
+ // Arrange & Act
+ PolylineEncoder encoder = new PolylineEncoder();
+
+ // Assert
+ Assert.AreEqual(512, encoder.Options.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that parameterized constructor creates encoder with specified options.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_WithOptions_CreatesEncoderWithSpecifiedOptions() {
+ // Arrange
+ PolylineEncodingOptions options = new PolylineEncodingOptions {
+ Precision = 6,
+ StackAllocLimit = 1024
+ };
+
+ // Act
+ PolylineEncoder encoder = new PolylineEncoder(options);
+
+ // Assert
+ Assert.IsNotNull(encoder);
+ Assert.AreSame(options, encoder.Options);
+ }
+
+ ///
+ /// Tests that parameterized constructor preserves custom precision.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_WithCustomPrecision_PreservesCustomPrecision() {
+ // Arrange
+ PolylineEncodingOptions options = new PolylineEncodingOptions {
+ Precision = 7
+ };
+
+ // Act
+ PolylineEncoder encoder = new PolylineEncoder(options);
+
+ // Assert
+ Assert.AreEqual(7u, encoder.Options.Precision);
+ }
+
+ ///
+ /// Tests that parameterized constructor preserves custom stack alloc limit.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_WithCustomStackAllocLimit_PreservesCustomStackAllocLimit() {
+ // Arrange
+ PolylineEncodingOptions options = new PolylineEncodingOptions {
+ StackAllocLimit = 2048
+ };
+
+ // Act
+ PolylineEncoder encoder = new PolylineEncoder(options);
+
+ // Assert
+ Assert.AreEqual(2048, encoder.Options.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that parameterized constructor throws ArgumentNullException when options is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_WithNullOptions_ThrowsArgumentNullException() {
+ // Arrange
+ PolylineEncodingOptions? options = null;
+
+ // Act & Assert
+ ArgumentNullException exception = Assert.ThrowsExactly(
+ () => new PolylineEncoder(options!));
+ Assert.AreEqual("options", exception.ParamName);
+ }
+
+ ///
+ /// Tests that Encode encodes a collection of coordinates into a polyline string.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_Encode_EncodesCoordinatesToPolyline() {
+ // Arrange
+ var encoder = new PolylineEncoder();
+ var coordinates = new[]
+ {
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95),
+ new Coordinate(43.252, -126.453)
+ };
+
+ // Act
+ Polyline polyline = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual("_p~iF~ps|U_ulLnnqC_mqNvxq`@", polyline.ToString());
+ }
+
+ ///
+ /// Tests that Encode throws ArgumentNullException when coordinates is null.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_Encode_NullCoordinates_ThrowsArgumentException() {
+ // Arrange
+ var encoder = new PolylineEncoder();
+ Coordinate[]? coordinates = null;
+
+ // Act & Assert
+ Assert.ThrowsExactly(() => encoder.Encode(coordinates));
+ }
+
+ ///
+ /// Tests that Encode throws ArgumentException when coordinates is empty.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_Encode_EmptyCoordinates_ThrowsArgumentException() {
+ // Arrange
+ var encoder = new PolylineEncoder();
+ var coordinates = Array.Empty();
+
+ // Act & Assert
+ Assert.ThrowsExactly(() => encoder.Encode(coordinates));
+ }
+
+ ///
+ /// Tests that Encode encodes a single coordinate correctly.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_Encode_SingleCoordinate_EncodesCorrectly() {
+ // Arrange
+ var encoder = new PolylineEncoder();
+ var coordinates = new[] { new Coordinate(38.5, -120.2) };
+
+ // Act
+ Polyline polyline = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual("_p~iF~ps|U", polyline.ToString());
+ }
+
+ ///
+ /// Tests that Encode encodes two coordinates correctly.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_Encode_TwoCoordinates_EncodesCorrectly() {
+ // Arrange
+ var encoder = new PolylineEncoder();
+ var coordinates = new[]
+ {
+ new Coordinate(38.5, -120.2),
+ new Coordinate(40.7, -120.95)
+ };
+
+ // Act
+ Polyline polyline = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.AreEqual("_p~iF~ps|U_ulLnnqC", polyline.ToString());
+ }
+
+ ///
+ /// Tests that Encode works with negative and zero coordinates.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void PolylineEncoder_Encode_NegativeAndZeroCoordinates_EncodesCorrectly() {
+ // Arrange
+ var encoder = new PolylineEncoder();
+ var coordinates = new[]
+ {
+ new Coordinate(0, 0),
+ new Coordinate(-45.12345, 179.99999)
+ };
+
+ // Act
+ Polyline polyline = encoder.Encode(coordinates);
+
+ // Assert
+ Assert.IsFalse(string.IsNullOrEmpty(polyline.ToString()));
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs
new file mode 100644
index 00000000..a5c3cf39
--- /dev/null
+++ b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs
@@ -0,0 +1,454 @@
+//
+// Copyright © Pete Sramek. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace PolylineAlgorithm.Tests;
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using PolylineAlgorithm.Tests.Properties;
+using System;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class PolylineEncodingOptionsBuilderTests {
+ ///
+ /// Tests that Create returns a new builder instance.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Create_ReturnsNewBuilder() {
+ // Act
+ PolylineEncodingOptionsBuilder result = PolylineEncodingOptionsBuilder.Create();
+
+ // Assert
+ Assert.IsNotNull(result);
+ }
+
+ ///
+ /// Tests that Create returns different instances on multiple calls.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Create_MultipleInvocations_ReturnsDifferentInstances() {
+ // Act
+ PolylineEncodingOptionsBuilder first = PolylineEncodingOptionsBuilder.Create();
+ PolylineEncodingOptionsBuilder second = PolylineEncodingOptionsBuilder.Create();
+
+ // Assert
+ Assert.AreNotSame(first, second);
+ }
+
+ ///
+ /// Tests that Build returns options with default values.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Build_WithDefaults_ReturnsOptionsWithDefaultValues() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(5u, result.Precision);
+ Assert.AreEqual(512, result.StackAllocLimit);
+ Assert.IsNotNull(result.LoggerFactory);
+ Assert.IsInstanceOfType(result.LoggerFactory);
+ }
+
+ ///
+ /// Tests that Build returns options with configured precision.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Build_WithCustomPrecision_ReturnsOptionsWithCustomPrecision() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithPrecision(7);
+
+ // Act
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(7u, result.Precision);
+ }
+
+ ///
+ /// Tests that Build returns options with configured stack alloc limit.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Build_WithCustomStackAllocLimit_ReturnsOptionsWithCustomStackAllocLimit() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithStackAllocLimit(1024);
+
+ // Act
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(1024, result.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that Build returns options with configured logger factory.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Build_WithCustomLoggerFactory_ReturnsOptionsWithCustomLoggerFactory() {
+ // Arrange
+ ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { });
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithLoggerFactory(loggerFactory);
+
+ // Act
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreSame(loggerFactory, result.LoggerFactory);
+
+ // Cleanup
+ loggerFactory.Dispose();
+ }
+
+ ///
+ /// Tests that Build returns options with all custom values.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Build_WithAllCustomValues_ReturnsOptionsWithAllCustomValues() {
+ // Arrange
+ ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { });
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithPrecision(10)
+ .WithStackAllocLimit(2048)
+ .WithLoggerFactory(loggerFactory);
+
+ // Act
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(10u, result.Precision);
+ Assert.AreEqual(2048, result.StackAllocLimit);
+ Assert.AreSame(loggerFactory, result.LoggerFactory);
+
+ // Cleanup
+ loggerFactory.Dispose();
+ }
+
+ ///
+ /// Tests that Build can be called multiple times on the same builder.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void Build_MultipleInvocations_ReturnsDifferentInstancesWithSameValues() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithPrecision(6);
+
+ // Act
+ PolylineEncodingOptions first = builder.Build();
+ PolylineEncodingOptions second = builder.Build();
+
+ // Assert
+ Assert.AreNotSame(first, second);
+ Assert.AreEqual(first.Precision, second.Precision);
+ Assert.AreEqual(first.StackAllocLimit, second.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that WithStackAllocLimit sets the value and returns the builder.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithStackAllocLimit_ValidValue_SetsValueAndReturnsSelf() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ PolylineEncodingOptionsBuilder result = builder.WithStackAllocLimit(256);
+
+ // Assert
+ Assert.AreSame(builder, result);
+ PolylineEncodingOptions options = builder.Build();
+ Assert.AreEqual(256, options.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that WithStackAllocLimit accepts minimum value of 1.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithStackAllocLimit_MinimumValue_SetsValue() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithStackAllocLimit(1);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(1, result.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that WithStackAllocLimit throws ArgumentOutOfRangeException for zero.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithStackAllocLimit_Zero_ThrowsArgumentOutOfRangeException() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act & Assert
+ try {
+ builder.WithStackAllocLimit(0);
+ Assert.Fail("Expected ArgumentOutOfRangeException was not thrown.");
+ } catch (ArgumentOutOfRangeException ex) {
+ Assert.AreEqual("stackAllocLimit", ex.ParamName);
+ }
+ }
+
+ ///
+ /// Tests that WithStackAllocLimit throws ArgumentOutOfRangeException for negative value.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithStackAllocLimit_NegativeValue_ThrowsArgumentOutOfRangeException() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act & Assert
+ try {
+ builder.WithStackAllocLimit(-10);
+ Assert.Fail("Expected ArgumentOutOfRangeException was not thrown.");
+ } catch (ArgumentOutOfRangeException ex) {
+ Assert.AreEqual("stackAllocLimit", ex.ParamName);
+ }
+ }
+
+ ///
+ /// Tests that WithStackAllocLimit accepts large value.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithStackAllocLimit_LargeValue_SetsValue() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithStackAllocLimit(100000);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(100000, result.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that WithStackAllocLimit can be called multiple times.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithStackAllocLimit_MultipleCalls_LastValueWins() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithStackAllocLimit(100)
+ .WithStackAllocLimit(200)
+ .WithStackAllocLimit(300);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(300, result.StackAllocLimit);
+ }
+
+ ///
+ /// Tests that WithPrecision sets the value and returns the builder.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithPrecision_ValidValue_SetsValueAndReturnsSelf() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ PolylineEncodingOptionsBuilder result = builder.WithPrecision(8);
+
+ // Assert
+ Assert.AreSame(builder, result);
+ PolylineEncodingOptions options = builder.Build();
+ Assert.AreEqual(8u, options.Precision);
+ }
+
+ ///
+ /// Tests that WithPrecision accepts zero value.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithPrecision_Zero_SetsValue() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithPrecision(0);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(0u, result.Precision);
+ }
+
+ ///
+ /// Tests that WithPrecision accepts maximum uint value.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithPrecision_MaxValue_SetsValue() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithPrecision(uint.MaxValue);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(uint.MaxValue, result.Precision);
+ }
+
+ ///
+ /// Tests that WithPrecision can be called multiple times.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithPrecision_MultipleCalls_LastValueWins() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithPrecision(5)
+ .WithPrecision(7)
+ .WithPrecision(9);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreEqual(9u, result.Precision);
+ }
+
+ ///
+ /// Tests that WithLoggerFactory sets the factory and returns the builder.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithLoggerFactory_ValidFactory_SetsValueAndReturnsSelf() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+ ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { });
+
+ // Act
+ PolylineEncodingOptionsBuilder result = builder.WithLoggerFactory(loggerFactory);
+
+ // Assert
+ Assert.AreSame(builder, result);
+ PolylineEncodingOptions options = builder.Build();
+ Assert.AreSame(loggerFactory, options.LoggerFactory);
+
+ // Cleanup
+ loggerFactory.Dispose();
+ }
+
+ ///
+ /// Tests that WithLoggerFactory with null uses NullLoggerFactory.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithLoggerFactory_Null_UsesNullLoggerFactory() {
+ // Arrange
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create();
+
+ // Act
+ builder.WithLoggerFactory(null!);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.IsNotNull(result.LoggerFactory);
+ Assert.IsInstanceOfType(result.LoggerFactory);
+ }
+
+ ///
+ /// Tests that WithLoggerFactory can replace a previously set factory.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithLoggerFactory_ReplacePreviousFactory_UpdatesValue() {
+ // Arrange
+ ILoggerFactory firstFactory = LoggerFactory.Create(builder => { });
+ ILoggerFactory secondFactory = LoggerFactory.Create(builder => { });
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithLoggerFactory(firstFactory);
+
+ // Act
+ builder.WithLoggerFactory(secondFactory);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.AreSame(secondFactory, result.LoggerFactory);
+
+ // Cleanup
+ firstFactory.Dispose();
+ secondFactory.Dispose();
+ }
+
+ ///
+ /// Tests that WithLoggerFactory can be set to null after setting a factory.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void WithLoggerFactory_NullAfterFactory_UsesNullLoggerFactory() {
+ // Arrange
+ ILoggerFactory factory = LoggerFactory.Create(builder => { });
+ PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create()
+ .WithLoggerFactory(factory);
+
+ // Act
+ builder.WithLoggerFactory(null!);
+ PolylineEncodingOptions result = builder.Build();
+
+ // Assert
+ Assert.IsInstanceOfType(result.LoggerFactory);
+
+ // Cleanup
+ factory.Dispose();
+ }
+
+ ///
+ /// Tests that builder supports method chaining for all methods.
+ ///
+ [TestMethod]
+ [TestCategory(Category.Unit)]
+ public void MethodChaining_AllMethods_ReturnsBuilderForChaining() {
+ // Arrange
+ ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { });
+
+ // Act
+ PolylineEncodingOptions result = PolylineEncodingOptionsBuilder.Create()
+ .WithPrecision(6)
+ .WithStackAllocLimit(1024)
+ .WithLoggerFactory(loggerFactory)
+ .Build();
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(6u, result.Precision);
+ Assert.AreEqual(1024, result.StackAllocLimit);
+ Assert.AreSame(loggerFactory, result.LoggerFactory);
+
+ // Cleanup
+ loggerFactory.Dispose();
+ }
+}
diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsTest.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsTest.cs
deleted file mode 100644
index 9b0fa679..00000000
--- a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsTest.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Logging.Testing;
-using PolylineAlgorithm.Tests.Fakes;
-
-[TestClass]
-public class PolylineEncodingOptionsTest {
- [TestMethod]
- public void Constructor_Parameterless_Ok() {
- // Arrange && Act
- var options = new PolylineEncodingOptions();
-
- // Assert
- Assert.AreEqual(64_000, options.MaxBufferSize);
- Assert.AreEqual(64_000 / sizeof(char), options.MaxBufferLength);
- Assert.IsInstanceOfType(options.LoggerFactory);
- }
-
- [TestMethod]
- public void Constructor_ValidOptions_Ok() {
- // Arrange
- var bufferSize = 32_000;
- var loggerFactory = new FakeLoggerFactory(new FakeLoggerProvider());
-
- // Act
- var options = new PolylineEncodingOptions() {
- MaxBufferSize = bufferSize,
- LoggerFactory = loggerFactory
- };
-
- // Assert
- Assert.AreEqual(bufferSize, options.MaxBufferSize);
- Assert.AreEqual(bufferSize / sizeof(char), options.MaxBufferLength);
- Assert.IsInstanceOfType(options.LoggerFactory);
- }
-}
diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingTest.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingTest.cs
deleted file mode 100644
index f851f409..00000000
--- a/tests/PolylineAlgorithm.Tests/PolylineEncodingTest.cs
+++ /dev/null
@@ -1,265 +0,0 @@
-//
-// Copyright © Pete Sramek. All rights reserved.
-// Licensed under the MIT License. See LICENSE file in the project root for full license information.
-//
-
-namespace PolylineAlgorithm.Tests;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using PolylineAlgorithm;
-using System;
-using System.Collections.Generic;
-
-[TestClass]
-public class PolylineEncodingTest {
- #region Dynamic Data Properties
-
- public static IEnumerable<(int variance, string polyline)> VariancePolylinePairs => [
- (0,"?"),
- (1,"A"),
- (-1,"@"),
- (16,"_@"),
- (-16,"^"),
- (511,"}^"),
- (-511,"|^"),
- (512,"__@"),
- (-512,"~^"),
- (16383,"}~^"),
- (-16383,"|~^"),
- (16384,"___@"),
- (-16384,"~~^"),
- (524287,"}~~^"),
- (-524287,"|~~^"),
- (524288,"____@"),
- (-524288,"~~~^"),
- (16777215,"}~~~^"),
- (-16777215,"|~~~^")
- ];
-
- public static IEnumerable<(double denormalized, int normalized, CoordinateValueType)> DenormalizedNormalizedPairs => [
- (0,0, CoordinateValueType.Latitude),
- (0,0, CoordinateValueType.Longitude),
- (1.23456,123456, CoordinateValueType.Latitude),
- (-1.23456,-123456, CoordinateValueType.Latitude),
- (1.23456,123456, CoordinateValueType.Longitude),
- (-1.23456,-123456, CoordinateValueType.Longitude),
- (90,9000000, CoordinateValueType.Latitude),
- (-90,-9000000, CoordinateValueType.Latitude),
- (90,9000000, CoordinateValueType.Longitude),
- (-90,-9000000, CoordinateValueType.Longitude),
- (180,18000000, CoordinateValueType.Longitude),
- (-180,-18000000, CoordinateValueType.Longitude)
- ];
-
- public static IEnumerable<(double denormalized, CoordinateValueType)> DenormalizedOutOfRangeValues => [
- (90.00001,CoordinateValueType.Latitude),
- (-90.00001,CoordinateValueType.Latitude),
- (180.00001,CoordinateValueType.Longitude),
- (-180.00001,CoordinateValueType.Longitude),
- (double.NaN,CoordinateValueType.Latitude),
- (double.NaN,CoordinateValueType.Longitude),
- (double.MinValue,CoordinateValueType.Latitude),
- (double.MaxValue,CoordinateValueType.Latitude),
- (double.MinValue,CoordinateValueType.Longitude),
- (double.MaxValue,CoordinateValueType.Longitude),
- (double.NegativeInfinity,CoordinateValueType.Latitude),
- (double.PositiveInfinity,CoordinateValueType.Latitude),
- (double.NegativeInfinity,CoordinateValueType.Longitude),
- (double.PositiveInfinity,CoordinateValueType.Longitude),
- ];
-
- public static IEnumerable<(int normalized, CoordinateValueType)> NormalizedOutOfRangeValues => [
- (9000001,CoordinateValueType.Latitude),
- (-9000001,CoordinateValueType.Latitude),
- (18000001,CoordinateValueType.Longitude),
- (-18000001,CoordinateValueType.Longitude),
- (int.MinValue,CoordinateValueType.Latitude),
- (int.MaxValue,CoordinateValueType.Latitude),
- (int.MinValue,CoordinateValueType.Longitude),
- (int.MaxValue,CoordinateValueType.Longitude),
- ];
-
- public static IEnumerable<(int variance, int charCount)> VarianceCharCountPairs => [
- (0, 1),
- (15, 1),
- (-16, 1),
- (16, 2),
- (-17,2),
- (511,2),
- (-512,2),
- (512,3),
- (-513,3),
- (16383,3),
- (-16384,3),
- (16384,4),
- (-16385,4),
- (524287,4),
- (-524288,4),
- (524288,5),
- (-524289,5),
- (16777215,5),
- (-16777216,5),
- (16777216,6),
- (-16777217,6),
- (int.MaxValue,6),
- (int.MinValue,6),
- ];
-
- #endregion
-
- [TestMethod]
- [DynamicData(nameof(DenormalizedNormalizedPairs), DynamicDataSourceType.Property)]
- public void Normalize_Equals_Expected(double denormalized, int expected, CoordinateValueType type) {
- // Arrange & Act
- int result = PolylineEncoding.Normalize(denormalized, type);
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
-
- [TestMethod]
- [DynamicData(nameof(DenormalizedNormalizedPairs), DynamicDataSourceType.Property)]
- public void Denormalize_Equals_Expected(double expected, int normalized, CoordinateValueType type) {
- // Arrange & Act
- double result = PolylineEncoding.Denormalize(normalized, type);
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [TestMethod]
- [DynamicData(nameof(VariancePolylinePairs), DynamicDataSourceType.Property)]
- public void TryWriteValue_StaticBuffer_Returns_True_Equals_Expected(int variance, string expected) {
- // Arrange
- int position = 0;
- Span buffer = stackalloc char[6];
-
- // Act
- bool result = PolylineEncoding.TryWriteValue(variance, ref buffer, ref position);
-
- // Assert
- Assert.IsTrue(result);
- Assert.AreEqual(expected.Length, position);
- Assert.AreEqual(expected, buffer[..position].ToString());
- }
-
-
- [TestMethod]
- [DynamicData(nameof(VariancePolylinePairs), DynamicDataSourceType.Property)]
- public void TryWriteValue_DynamicBuffer_Returns_True_Equals_Expected(int variance, string expected) {
- // Arrange
- int position = 0;
- int required = PolylineEncoding.GetCharCount(variance);
- Span buffer = stackalloc char[required];
-
- // Act
- bool result = PolylineEncoding.TryWriteValue(variance, ref buffer, ref position);
-
- // Assert
- Assert.IsTrue(result);
- Assert.AreEqual(required, position);
- Assert.AreEqual(expected.Length, position);
- Assert.AreEqual(expected, buffer[..position].ToString());
- }
-
- [TestMethod]
- [DynamicData(nameof(VariancePolylinePairs), DynamicDataSourceType.Property)]
- public void TryWriteValue_BufferTooSmall_Returns_False(int variance, string _) {
- // Arrange
- int position = 0;
- int required = PolylineEncoding.GetCharCount(variance);
- Span buffer = stackalloc char[required - 1];
-
- // Act
- bool result = PolylineEncoding.TryWriteValue(variance, ref buffer, ref position);
-
- // Assert
- Assert.IsFalse(result);
- }
-
- [TestMethod]
- [DynamicData(nameof(VariancePolylinePairs), DynamicDataSourceType.Property)]
- public void TryReadValue_Ok(int expected, string polyline) {
- // Arrange
- int position = 0;
- int variance = 0;
- var buffer = polyline.AsMemory();
-
- // Act
- bool result = PolylineEncoding.TryReadValue(ref variance, ref buffer, ref position);
-
- // Assert
- Assert.IsTrue(result);
- Assert.AreEqual(buffer.Length, position);
- Assert.AreEqual(expected, variance);
- }
-
- [TestMethod]
- public void TryReadValue_EmptyBuffer_Returns_False() {
- // Arrange
- int variance = 0;
- int position = 0;
- ReadOnlyMemory buffer = Memory